diff --git a/mRemoteNG/App/Export.cs b/mRemoteNG/App/Export.cs index ced906c42..ecf935b8a 100644 --- a/mRemoteNG/App/Export.cs +++ b/mRemoteNG/App/Export.cs @@ -83,7 +83,7 @@ namespace mRemoteNG.App case SaveFormat.mRXML: var cryptographyProvider = new CryptoProviderFactoryFromSettings().Build(); var rootNode = exportTarget.GetRootParent() as RootNodeInfo; - var connectionNodeSerializer = new XmlConnectionNodeSerializer27( + var connectionNodeSerializer = new XmlConnectionNodeSerializer28( cryptographyProvider, rootNode?.PasswordString .ConvertToSecureString() ?? diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs index f6fe3bc3f..a3a269a28 100644 --- a/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsDeserializerMremotengFormat.cs @@ -350,7 +350,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv if (headers.Contains("RedirectDiskDrives")) { - if (bool.TryParse(connectionCsv[headers.IndexOf("RedirectDiskDrives")], out bool value)) + if (Enum.TryParse(connectionCsv[headers.IndexOf("RedirectDiskDrives")], out RDPDiskDrives value)) connectionRecord.RedirectDiskDrives = value; } @@ -611,6 +611,12 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv if (bool.TryParse(connectionCsv[headers.IndexOf("InheritRedirectDiskDrives")], out bool value)) connectionRecord.Inheritance.RedirectDiskDrives = value; } + + if (headers.Contains("InheritRedirectDiskDrivesCustom")) + { + if (bool.TryParse(connectionCsv[headers.IndexOf("InheritRedirectDiskDrivesCustom")], out bool value)) + connectionRecord.Inheritance.RedirectDiskDrivesCustom = value; + } if (headers.Contains("InheritRedirectKeys")) { diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs index e12341b6b..ee56c475e 100644 --- a/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/Csv/CsvConnectionsSerializerMremotengFormat.cs @@ -18,7 +18,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv private readonly SaveFilter _saveFilter; private readonly ICredentialRepositoryList _credentialRepositoryList; - public Version Version { get; } = new Version(2, 7); + public Version Version { get; } = new Version(2, 8); public CsvConnectionsSerializerMremotengFormat(SaveFilter saveFilter, ICredentialRepositoryList credentialRepositoryList) @@ -60,7 +60,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv sb.Append("Hostname;Port;VmId;Protocol;SSHTunnelConnectionName;OpeningCommand;SSHOptions;PuttySession;ConnectToConsole;UseCredSsp;UseRestrictedAdmin;UseRCG;UseVmId;UseEnhancedMode;RenderingEngine;RDPAuthenticationLevel;" + "LoadBalanceInfo;Colors;Resolution;AutomaticResize;DisplayWallpaper;DisplayThemes;EnableFontSmoothing;EnableDesktopComposition;DisableFullWindowDrag;DisableMenuAnimations;DisableCursorShadow;DisableCursorBlinking;" + - "CacheBitmaps;RedirectDiskDrives;RedirectPorts;RedirectPrinters;RedirectClipboard;RedirectSmartCards;RedirectSound;RedirectKeys;" + + "CacheBitmaps;RedirectDiskDrives;RedirectDiskDrivesCustomRedirectPorts;RedirectPrinters;RedirectClipboard;RedirectSmartCards;RedirectSound;RedirectKeys;" + "PreExtApp;PostExtApp;MacAddress;UserField;ExtApp;Favorite;VNCCompression;VNCEncoding;VNCAuthMode;VNCProxyType;VNCProxyIP;" + "VNCProxyPort;VNCProxyUsername;VNCProxyPassword;VNCColors;VNCSmartSizeMode;VNCViewOnly;RDGatewayUsageMethod;RDGatewayHostname;" + "RDGatewayUseConnectionCredentials;RDGatewayUsername;RDGatewayPassword;RDGatewayDomain;RDGatewayExternalCredentialProvider;RDGatewayUserViaAPI;RedirectAudioCapture;RdpVersion;RDPStartProgram;RDPStartProgramWorkDir;UserViaAPI;EC2InstanceId;EC2Region;ExternalCredentialProvider;ExternalAddressProvider;"); @@ -68,7 +68,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv if (_saveFilter.SaveInheritance) sb.Append("InheritCacheBitmaps;InheritColors;InheritDescription;InheritDisplayThemes;InheritDisplayWallpaper;" + "InheritEnableFontSmoothing;InheritEnableDesktopComposition;InheritDisableFullWindowDrag;InheritDisableMenuAnimations;InheritDisableCursorShadow;InheritDisableCursorBlinking;InheritDomain;InheritIcon;InheritPanel;InheritPassword;InheritPort;" + - "InheritProtocol;InheritSSHTunnelConnectionName;InheritOpeningCommand;InheritSSHOptions;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" + + "InheritProtocol;InheritSSHTunnelConnectionName;InheritOpeningCommand;InheritSSHOptions;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectDiskDrivesCustom;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" + "InheritRedirectClipboard;InheritRedirectSmartCards;InheritRedirectSound;InheritResolution;InheritAutomaticResize;" + "InheritUseConsoleSession;InheritUseCredSsp;InheritUseRestrictedAdmin;InheritUseRCG;InheritUseVmId;InheritUseEnhancedMode;InheritVmId;InheritRenderingEngine;InheritUsername;" + "InheritRDPAuthenticationLevel;InheritLoadBalanceInfo;InheritPreExtApp;InheritPostExtApp;InheritMacAddress;InheritUserField;" + @@ -147,6 +147,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv .Append(FormatForCsv(con.DisableCursorBlinking)) .Append(FormatForCsv(con.CacheBitmaps)) .Append(FormatForCsv(con.RedirectDiskDrives)) + .Append(FormatForCsv(con.RedirectDiskDrivesCustom)) .Append(FormatForCsv(con.RedirectPorts)) .Append(FormatForCsv(con.RedirectPrinters)) .Append(FormatForCsv(con.RedirectClipboard)) @@ -215,6 +216,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Csv .Append(FormatForCsv(con.Inheritance.SSHOptions)) .Append(FormatForCsv(con.Inheritance.PuttySession)) .Append(FormatForCsv(con.Inheritance.RedirectDiskDrives)) + .Append(FormatForCsv(con.Inheritance.RedirectDiskDrivesCustom)) .Append(FormatForCsv(con.Inheritance.RedirectKeys)) .Append(FormatForCsv(con.Inheritance.RedirectPorts)) .Append(FormatForCsv(con.Inheritance.RedirectPrinters)) diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs index b40c210a8..5f29a2ef2 100644 --- a/mRemoteNG/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/MsSql/DataTableDeserializer.cs @@ -124,7 +124,8 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql connectionInfo.DisableCursorShadow = (bool)dataRow["DisableCursorShadow"]; connectionInfo.DisableCursorBlinking = (bool)dataRow["DisableCursorBlinking"]; connectionInfo.CacheBitmaps = (bool)dataRow["CacheBitmaps"]; - connectionInfo.RedirectDiskDrives = (bool)dataRow["RedirectDiskDrives"]; + connectionInfo.RedirectDiskDrives = (RDPDiskDrives)Enum.Parse(typeof(RDPDiskDrives), (string)dataRow["RedirectDiskDrives"]); + connectionInfo.RedirectDiskDrivesCustom = (string)dataRow["RedirectDiskDrivesCustom"]; connectionInfo.RedirectPorts = (bool)dataRow["RedirectPorts"]; connectionInfo.RedirectPrinters = (bool)dataRow["RedirectPrinters"]; connectionInfo.RedirectClipboard = (bool)dataRow["RedirectClipboard"]; @@ -189,6 +190,7 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.MsSql connectionInfo.Inheritance.SSHOptions = (bool)dataRow["InheritSSHOptions"]; connectionInfo.Inheritance.PuttySession = (bool)dataRow["InheritPuttySession"]; connectionInfo.Inheritance.RedirectDiskDrives = (bool)dataRow["InheritRedirectDiskDrives"]; + connectionInfo.Inheritance.RedirectDiskDrivesCustom = (bool)dataRow["InheritRedirectDiskDrivesCustom"]; connectionInfo.Inheritance.RedirectKeys = (bool)dataRow["InheritRedirectKeys"]; connectionInfo.Inheritance.RedirectPorts = (bool)dataRow["InheritRedirectPorts"]; connectionInfo.Inheritance.RedirectPrinters = (bool)dataRow["InheritRedirectPrinters"]; diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer28.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer28.cs new file mode 100644 index 000000000..f073a515b --- /dev/null +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionNodeSerializer28.cs @@ -0,0 +1,327 @@ +using System; +using System.Runtime.Versioning; +using System.Security; +using System.Xml.Linq; +using mRemoteNG.App; +using mRemoteNG.Connection; +using mRemoteNG.Container; +using mRemoteNG.Security; + +namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml +{ + // ReSharper disable once InconsistentNaming + [SupportedOSPlatform("windows")] + public class XmlConnectionNodeSerializer28 : ISerializer + { + private readonly ICryptographyProvider _cryptographyProvider; + private readonly SecureString _encryptionKey; + private readonly SaveFilter _saveFilter; + + public Version Version { get; } = new Version(2, 8); + + public XmlConnectionNodeSerializer28(ICryptographyProvider cryptographyProvider, + SecureString encryptionKey, + SaveFilter saveFilter) + { + if (cryptographyProvider == null) + throw new ArgumentNullException(nameof(cryptographyProvider)); + if (encryptionKey == null) + throw new ArgumentNullException(nameof(encryptionKey)); + if (saveFilter == null) + throw new ArgumentNullException(nameof(saveFilter)); + + _cryptographyProvider = cryptographyProvider; + _encryptionKey = encryptionKey; + _saveFilter = saveFilter; + } + + public XElement Serialize(ConnectionInfo connectionInfo) + { + var element = new XElement(XName.Get("Node", "")); + SetElementAttributes(element, connectionInfo); + SetInheritanceAttributes(element, connectionInfo); + return element; + } + + private void SetElementAttributes(XContainer element, ConnectionInfo connectionInfo) + { + var nodeAsContainer = connectionInfo as ContainerInfo; + element.Add(new XAttribute("Name", connectionInfo.Name)); + element.Add(new XAttribute("VmId", connectionInfo.VmId)); + element.Add(new XAttribute("UseVmId", connectionInfo.UseVmId)); + element.Add(new XAttribute("UseEnhancedMode", connectionInfo.UseVmId)); + element.Add(new XAttribute("Type", connectionInfo.GetTreeNodeType().ToString())); + if (nodeAsContainer != null) + element.Add(new XAttribute("Expanded", nodeAsContainer.IsExpanded.ToString().ToLowerInvariant())); + element.Add(new XAttribute("Descr", connectionInfo.Description)); + element.Add(new XAttribute("Icon", connectionInfo.Icon)); + element.Add(new XAttribute("Panel", connectionInfo.Panel)); + element.Add(new XAttribute("Id", connectionInfo.ConstantID)); + + if (!Runtime.UseCredentialManager) + { + element.Add(_saveFilter.SaveUsername + ? new XAttribute("Username", connectionInfo.Username) + : new XAttribute("Username", "")); + + element.Add(_saveFilter.SaveDomain + ? new XAttribute("Domain", connectionInfo.Domain) + : new XAttribute("Domain", "")); + + if (_saveFilter.SavePassword && !connectionInfo.Inheritance.Password) + element.Add(new XAttribute("Password", _cryptographyProvider.Encrypt(connectionInfo.Password, _encryptionKey))); + else + element.Add(new XAttribute("Password", "")); + } + + element.Add(new XAttribute("Hostname", connectionInfo.Hostname)); + element.Add(new XAttribute("Protocol", connectionInfo.Protocol)); + element.Add(new XAttribute("RdpVersion", connectionInfo.RdpVersion.ToString().ToLowerInvariant())); + element.Add(new XAttribute("SSHTunnelConnectionName", connectionInfo.SSHTunnelConnectionName)); + element.Add(new XAttribute("OpeningCommand", connectionInfo.OpeningCommand)); + element.Add(new XAttribute("SSHOptions", connectionInfo.SSHOptions)); + element.Add(new XAttribute("PuttySession", connectionInfo.PuttySession)); + element.Add(new XAttribute("Port", connectionInfo.Port)); + element.Add(new XAttribute("ConnectToConsole", connectionInfo.UseConsoleSession.ToString().ToLowerInvariant())); + element.Add(new XAttribute("UseCredSsp", connectionInfo.UseCredSsp.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RenderingEngine", connectionInfo.RenderingEngine)); + element.Add(new XAttribute("RDPAuthenticationLevel", connectionInfo.RDPAuthenticationLevel)); + element.Add(new XAttribute("RDPMinutesToIdleTimeout", connectionInfo.RDPMinutesToIdleTimeout)); + element.Add(new XAttribute("RDPAlertIdleTimeout", connectionInfo.RDPAlertIdleTimeout.ToString().ToLowerInvariant())); + element.Add(new XAttribute("LoadBalanceInfo", connectionInfo.LoadBalanceInfo)); + element.Add(new XAttribute("Colors", connectionInfo.Colors)); + element.Add(new XAttribute("Resolution", connectionInfo.Resolution)); + element.Add(new XAttribute("AutomaticResize", connectionInfo.AutomaticResize.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisplayWallpaper", connectionInfo.DisplayWallpaper.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisplayThemes", connectionInfo.DisplayThemes.ToString().ToLowerInvariant())); + element.Add(new XAttribute("EnableFontSmoothing", connectionInfo.EnableFontSmoothing.ToString().ToLowerInvariant())); + element.Add(new XAttribute("EnableDesktopComposition", connectionInfo.EnableDesktopComposition.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisableFullWindowDrag", connectionInfo.DisableFullWindowDrag.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisableMenuAnimations", connectionInfo.DisableMenuAnimations.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisableCursorShadow", connectionInfo.DisableCursorShadow.ToString().ToLowerInvariant())); + element.Add(new XAttribute("DisableCursorBlinking", connectionInfo.DisableCursorBlinking.ToString().ToLowerInvariant())); + element.Add(new XAttribute("CacheBitmaps", connectionInfo.CacheBitmaps.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectDiskDrives", connectionInfo.RedirectDiskDrives)); + element.Add(new XAttribute("RedirectDiskDrivesCustom", connectionInfo.RedirectDiskDrivesCustom)); + element.Add(new XAttribute("RedirectPorts", connectionInfo.RedirectPorts.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectPrinters", connectionInfo.RedirectPrinters.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectClipboard", connectionInfo.RedirectClipboard.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectSmartCards", connectionInfo.RedirectSmartCards.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectSound", connectionInfo.RedirectSound.ToString())); + element.Add(new XAttribute("SoundQuality", connectionInfo.SoundQuality.ToString())); + element.Add(new XAttribute("RedirectAudioCapture", connectionInfo.RedirectAudioCapture.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RedirectKeys", connectionInfo.RedirectKeys.ToString().ToLowerInvariant())); + element.Add(new XAttribute("Connected", (connectionInfo.OpenConnections.Count > 0).ToString().ToLowerInvariant())); + element.Add(new XAttribute("PreExtApp", connectionInfo.PreExtApp)); + element.Add(new XAttribute("PostExtApp", connectionInfo.PostExtApp)); + element.Add(new XAttribute("MacAddress", connectionInfo.MacAddress)); + element.Add(new XAttribute("UserField", connectionInfo.UserField)); + element.Add(new XAttribute("Favorite", connectionInfo.Favorite)); + element.Add(new XAttribute("ExtApp", connectionInfo.ExtApp)); + element.Add(new XAttribute("StartProgram", connectionInfo.RDPStartProgram)); + element.Add(new XAttribute("StartProgramWorkDir", connectionInfo.RDPStartProgramWorkDir)); + element.Add(new XAttribute("VNCCompression", connectionInfo.VNCCompression)); + element.Add(new XAttribute("VNCEncoding", connectionInfo.VNCEncoding)); + element.Add(new XAttribute("VNCAuthMode", connectionInfo.VNCAuthMode)); + element.Add(new XAttribute("VNCProxyType", connectionInfo.VNCProxyType)); + element.Add(new XAttribute("VNCProxyIP", connectionInfo.VNCProxyIP)); + element.Add(new XAttribute("VNCProxyPort", connectionInfo.VNCProxyPort)); + + element.Add(_saveFilter.SaveUsername + ? new XAttribute("VNCProxyUsername", connectionInfo.VNCProxyUsername) + : new XAttribute("VNCProxyUsername", "")); + + element.Add(_saveFilter.SavePassword + ? new XAttribute("VNCProxyPassword", _cryptographyProvider.Encrypt(connectionInfo.VNCProxyPassword, _encryptionKey)) + : new XAttribute("VNCProxyPassword", "")); + + element.Add(new XAttribute("VNCColors", connectionInfo.VNCColors)); + element.Add(new XAttribute("VNCSmartSizeMode", connectionInfo.VNCSmartSizeMode)); + element.Add(new XAttribute("VNCViewOnly", connectionInfo.VNCViewOnly.ToString().ToLowerInvariant())); + element.Add(new XAttribute("RDGatewayUsageMethod", connectionInfo.RDGatewayUsageMethod)); + element.Add(new XAttribute("RDGatewayHostname", connectionInfo.RDGatewayHostname)); + element.Add(new XAttribute("RDGatewayUseConnectionCredentials", connectionInfo.RDGatewayUseConnectionCredentials)); + element.Add(new XAttribute("RDGatewayExternalCredentialProvider", connectionInfo.RDGatewayExternalCredentialProvider)); + element.Add(new XAttribute("RDGatewayUserViaAPI", connectionInfo.RDGatewayUserViaAPI)); + + element.Add(_saveFilter.SaveUsername + ? new XAttribute("RDGatewayUsername", connectionInfo.RDGatewayUsername) + : new XAttribute("RDGatewayUsername", "")); + + element.Add(_saveFilter.SavePassword + ? new XAttribute("RDGatewayPassword", _cryptographyProvider.Encrypt(connectionInfo.RDGatewayPassword, _encryptionKey)) + : new XAttribute("RDGatewayPassword", "")); + + element.Add(_saveFilter.SaveDomain + ? new XAttribute("RDGatewayDomain", connectionInfo.RDGatewayDomain) + : new XAttribute("RDGatewayDomain", "")); + + element.Add(new XAttribute("UseRCG", connectionInfo.UseRCG)); + element.Add(new XAttribute("UseRestrictedAdmin", connectionInfo.UseRestrictedAdmin)); + + element.Add(new XAttribute("UserViaAPI", connectionInfo.UserViaAPI)); + element.Add(new XAttribute("EC2InstanceId", connectionInfo.EC2InstanceId)); + element.Add(new XAttribute("EC2Region", connectionInfo.EC2Region)); + element.Add(new XAttribute("ExternalCredentialProvider", connectionInfo.ExternalCredentialProvider)); + element.Add(new XAttribute("ExternalAddressProvider", connectionInfo.ExternalAddressProvider)); + } + + private void SetInheritanceAttributes(XContainer element, IInheritable connectionInfo) + { + if (_saveFilter.SaveInheritance) + { + var inheritance = connectionInfo.Inheritance; + + if (inheritance.CacheBitmaps) + element.Add(new XAttribute("InheritCacheBitmaps", inheritance.CacheBitmaps.ToString().ToLowerInvariant())); + if (inheritance.Colors) + element.Add(new XAttribute("InheritColors", inheritance.Colors.ToString().ToLowerInvariant())); + if (inheritance.Description) + element.Add(new XAttribute("InheritDescription", inheritance.Description.ToString().ToLowerInvariant())); + if (inheritance.DisplayThemes) + element.Add(new XAttribute("InheritDisplayThemes", inheritance.DisplayThemes.ToString().ToLowerInvariant())); + if (inheritance.DisplayWallpaper) + element.Add(new XAttribute("InheritDisplayWallpaper", inheritance.DisplayWallpaper.ToString().ToLowerInvariant())); + if (inheritance.EnableFontSmoothing) + element.Add(new XAttribute("InheritEnableFontSmoothing", inheritance.EnableFontSmoothing.ToString().ToLowerInvariant())); + if (inheritance.EnableDesktopComposition) + element.Add(new XAttribute("InheritEnableDesktopComposition", inheritance.EnableDesktopComposition.ToString().ToLowerInvariant())); + if (inheritance.DisableFullWindowDrag) + element.Add(new XAttribute("InheritDisableFullWindowDrag", inheritance.DisableFullWindowDrag.ToString().ToLowerInvariant())); + if (inheritance.DisableMenuAnimations) + element.Add(new XAttribute("InheritDisableMenuAnimations", inheritance.DisableMenuAnimations.ToString().ToLowerInvariant())); + if (inheritance.DisableCursorShadow) + element.Add(new XAttribute("InheritDisableCursorShadow", inheritance.DisableCursorShadow.ToString().ToLowerInvariant())); + if (inheritance.DisableCursorBlinking) + element.Add(new XAttribute("InheritDisableCursorBlinking", inheritance.DisableCursorBlinking.ToString().ToLowerInvariant())); + if (inheritance.Domain) + element.Add(new XAttribute("InheritDomain", inheritance.Domain.ToString().ToLowerInvariant())); + if (inheritance.Icon) + element.Add(new XAttribute("InheritIcon", inheritance.Icon.ToString().ToLowerInvariant())); + if (inheritance.Panel) + element.Add(new XAttribute("InheritPanel", inheritance.Panel.ToString().ToLowerInvariant())); + if (inheritance.Password) + element.Add(new XAttribute("InheritPassword", inheritance.Password.ToString().ToLowerInvariant())); + if (inheritance.Port) + element.Add(new XAttribute("InheritPort", inheritance.Port.ToString().ToLowerInvariant())); + if (inheritance.Protocol) + element.Add(new XAttribute("InheritProtocol", inheritance.Protocol.ToString().ToLowerInvariant())); + if (inheritance.RdpVersion) + element.Add(new XAttribute("InheritRdpVersion", inheritance.RdpVersion.ToString().ToLowerInvariant())); + if (inheritance.SSHTunnelConnectionName) + element.Add(new XAttribute("InheritSSHTunnelConnectionName", inheritance.SSHTunnelConnectionName.ToString().ToLowerInvariant())); + if (inheritance.OpeningCommand) + element.Add(new XAttribute("InheritOpeningCommand", inheritance.OpeningCommand.ToString().ToLowerInvariant())); + if (inheritance.SSHOptions) + element.Add(new XAttribute("InheritSSHOptions", inheritance.SSHOptions.ToString().ToLowerInvariant())); + if (inheritance.PuttySession) + element.Add(new XAttribute("InheritPuttySession", inheritance.PuttySession.ToString().ToLowerInvariant())); + if (inheritance.RedirectDiskDrives) + element.Add(new XAttribute("InheritRedirectDiskDrives", inheritance.RedirectDiskDrives.ToString().ToLowerInvariant())); + if (inheritance.RedirectDiskDrivesCustom) + element.Add(new XAttribute("InheritRedirectDiskDrivesCustom", inheritance.RedirectDiskDrivesCustom.ToString().ToLowerInvariant())); + if (inheritance.RedirectKeys) + element.Add(new XAttribute("InheritRedirectKeys", inheritance.RedirectKeys.ToString().ToLowerInvariant())); + if (inheritance.RedirectPorts) + element.Add(new XAttribute("InheritRedirectPorts", inheritance.RedirectPorts.ToString().ToLowerInvariant())); + if (inheritance.RedirectPrinters) + element.Add(new XAttribute("InheritRedirectPrinters", inheritance.RedirectPrinters.ToString().ToLowerInvariant())); + if (inheritance.RedirectClipboard) + element.Add(new XAttribute("InheritRedirectClipboard", inheritance.RedirectClipboard.ToString().ToLowerInvariant())); + if (inheritance.RedirectSmartCards) + element.Add(new XAttribute("InheritRedirectSmartCards", inheritance.RedirectSmartCards.ToString().ToLowerInvariant())); + if (inheritance.RedirectSound) + element.Add(new XAttribute("InheritRedirectSound", inheritance.RedirectSound.ToString().ToLowerInvariant())); + if (inheritance.SoundQuality) + element.Add(new XAttribute("InheritSoundQuality", inheritance.SoundQuality.ToString().ToLowerInvariant())); + if (inheritance.RedirectAudioCapture) + element.Add(new XAttribute("InheritRedirectAudioCapture", inheritance.RedirectAudioCapture.ToString().ToLowerInvariant())); + if (inheritance.Resolution) + element.Add(new XAttribute("InheritResolution", inheritance.Resolution.ToString().ToLowerInvariant())); + if (inheritance.AutomaticResize) + element.Add(new XAttribute("InheritAutomaticResize", inheritance.AutomaticResize.ToString().ToLowerInvariant())); + if (inheritance.UseConsoleSession) + element.Add(new XAttribute("InheritUseConsoleSession", inheritance.UseConsoleSession.ToString().ToLowerInvariant())); + if (inheritance.UseCredSsp) + element.Add(new XAttribute("InheritUseCredSsp", inheritance.UseCredSsp.ToString().ToLowerInvariant())); + if (inheritance.RenderingEngine) + element.Add(new XAttribute("InheritRenderingEngine", inheritance.RenderingEngine.ToString().ToLowerInvariant())); + if (inheritance.Username) + element.Add(new XAttribute("InheritUsername", inheritance.Username.ToString().ToLowerInvariant())); + if (inheritance.RDPAuthenticationLevel) + element.Add(new XAttribute("InheritRDPAuthenticationLevel", inheritance.RDPAuthenticationLevel.ToString().ToLowerInvariant())); + if (inheritance.RDPMinutesToIdleTimeout) + element.Add(new XAttribute("InheritRDPMinutesToIdleTimeout", inheritance.RDPMinutesToIdleTimeout.ToString().ToLowerInvariant())); + if (inheritance.RDPAlertIdleTimeout) + element.Add(new XAttribute("InheritRDPAlertIdleTimeout", inheritance.RDPAlertIdleTimeout.ToString().ToLowerInvariant())); + if (inheritance.LoadBalanceInfo) + element.Add(new XAttribute("InheritLoadBalanceInfo", inheritance.LoadBalanceInfo.ToString().ToLowerInvariant())); + if (inheritance.PreExtApp) + element.Add(new XAttribute("InheritPreExtApp", inheritance.PreExtApp.ToString().ToLowerInvariant())); + if (inheritance.PostExtApp) + element.Add(new XAttribute("InheritPostExtApp", inheritance.PostExtApp.ToString().ToLowerInvariant())); + if (inheritance.MacAddress) + element.Add(new XAttribute("InheritMacAddress", inheritance.MacAddress.ToString().ToLowerInvariant())); + if (inheritance.UserField) + element.Add(new XAttribute("InheritUserField", inheritance.UserField.ToString().ToLowerInvariant())); + if (inheritance.Favorite) + element.Add(new XAttribute("InheritFavorite", inheritance.Favorite.ToString().ToLowerInvariant())); + if (inheritance.ExtApp) + element.Add(new XAttribute("InheritExtApp", inheritance.ExtApp.ToString().ToLowerInvariant())); + if (inheritance.VNCCompression) + element.Add(new XAttribute("InheritVNCCompression", inheritance.VNCCompression.ToString().ToLowerInvariant())); + if (inheritance.VNCEncoding) + element.Add(new XAttribute("InheritVNCEncoding", inheritance.VNCEncoding.ToString().ToLowerInvariant())); + if (inheritance.VNCAuthMode) + element.Add(new XAttribute("InheritVNCAuthMode", inheritance.VNCAuthMode.ToString().ToLowerInvariant())); + if (inheritance.VNCProxyType) + element.Add(new XAttribute("InheritVNCProxyType", inheritance.VNCProxyType.ToString().ToLowerInvariant())); + if (inheritance.VNCProxyIP) + element.Add(new XAttribute("InheritVNCProxyIP", inheritance.VNCProxyIP.ToString().ToLowerInvariant())); + if (inheritance.VNCProxyPort) + element.Add(new XAttribute("InheritVNCProxyPort", inheritance.VNCProxyPort.ToString().ToLowerInvariant())); + if (inheritance.VNCProxyUsername) + element.Add(new XAttribute("InheritVNCProxyUsername", inheritance.VNCProxyUsername.ToString().ToLowerInvariant())); + if (inheritance.VNCProxyPassword) + element.Add(new XAttribute("InheritVNCProxyPassword", inheritance.VNCProxyPassword.ToString().ToLowerInvariant())); + if (inheritance.VNCColors) + element.Add(new XAttribute("InheritVNCColors", inheritance.VNCColors.ToString().ToLowerInvariant())); + if (inheritance.VNCSmartSizeMode) + element.Add(new XAttribute("InheritVNCSmartSizeMode", inheritance.VNCSmartSizeMode.ToString().ToLowerInvariant())); + if (inheritance.VNCViewOnly) + element.Add(new XAttribute("InheritVNCViewOnly", inheritance.VNCViewOnly.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayUsageMethod) + element.Add(new XAttribute("InheritRDGatewayUsageMethod", inheritance.RDGatewayUsageMethod.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayHostname) + element.Add(new XAttribute("InheritRDGatewayHostname", inheritance.RDGatewayHostname.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayUseConnectionCredentials) + element.Add(new XAttribute("InheritRDGatewayUseConnectionCredentials", inheritance.RDGatewayUseConnectionCredentials.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayUsername) + element.Add(new XAttribute("InheritRDGatewayUsername", inheritance.RDGatewayUsername.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayPassword) + element.Add(new XAttribute("InheritRDGatewayPassword", inheritance.RDGatewayPassword.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayDomain) + element.Add(new XAttribute("InheritRDGatewayDomain", inheritance.RDGatewayDomain.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayExternalCredentialProvider) + element.Add(new XAttribute("InheritRDGatewayExternalCredentialProvider", inheritance.RDGatewayExternalCredentialProvider.ToString().ToLowerInvariant())); + if (inheritance.RDGatewayUserViaAPI) + element.Add(new XAttribute("InheritRDGatewayUserViaAPI", inheritance.RDGatewayUserViaAPI.ToString().ToLowerInvariant())); + + if (inheritance.VmId) + element.Add(new XAttribute("InheritVmId", inheritance.VmId.ToString().ToLowerInvariant())); + if (inheritance.UseVmId) + element.Add(new XAttribute("InheritUseVmId", inheritance.UseVmId.ToString().ToLowerInvariant())); + if (inheritance.UseEnhancedMode) + element.Add(new XAttribute("InheritUseEnhancedMode", inheritance.UseEnhancedMode.ToString().ToLowerInvariant())); + if (inheritance.ExternalCredentialProvider) + element.Add(new XAttribute("InheritExternalCredentialProvider", inheritance.ExternalCredentialProvider.ToString().ToLowerInvariant())); + if (inheritance.UserViaAPI) + element.Add(new XAttribute("InheritUserViaAPI", inheritance.UserViaAPI.ToString().ToLowerInvariant())); + if (inheritance.UseRCG) + element.Add(new XAttribute("InheritUseRCG", inheritance.UseRCG.ToString().ToLowerInvariant())); + if (inheritance.UseRestrictedAdmin) + element.Add(new XAttribute("InheritUseRestrictedAdmin", inheritance.UseRestrictedAdmin.ToString().ToLowerInvariant())); + } + } + } +} \ No newline at end of file diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs index bee10b50b..ef7771984 100644 --- a/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/Xml/XmlConnectionsDeserializer.cs @@ -291,14 +291,13 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml if (_confVersion >= 0.5) { - connectionInfo.RedirectDiskDrives = xmlnode.GetAttributeAsBool("RedirectDiskDrives"); connectionInfo.RedirectPrinters = xmlnode.GetAttributeAsBool("RedirectPrinters"); connectionInfo.RedirectPorts = xmlnode.GetAttributeAsBool("RedirectPorts"); connectionInfo.RedirectSmartCards = xmlnode.GetAttributeAsBool("RedirectSmartCards"); } else { - connectionInfo.RedirectDiskDrives = false; + connectionInfo.RedirectDiskDrives = RDPDiskDrives.None; connectionInfo.RedirectPrinters = false; connectionInfo.RedirectPorts = false; connectionInfo.RedirectSmartCards = false; @@ -570,6 +569,21 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Xml connectionInfo.Inheritance.RDGatewayExternalCredentialProvider = xmlnode.GetAttributeAsBool("InheritRDGatewayExternalCredentialProvider"); connectionInfo.Inheritance.RDGatewayUserViaAPI = xmlnode.GetAttributeAsBool("InheritRDGatewayUserViaAPI"); } + if (_confVersion >= 2.8) + { + connectionInfo.RedirectDiskDrives = xmlnode.GetAttributeAsEnum("RedirectDiskDrives"); + connectionInfo.RedirectDiskDrivesCustom = xmlnode.GetAttributeAsString("RedirectDiskDrivesCustom"); + connectionInfo.Inheritance.RedirectDiskDrivesCustom = xmlnode.GetAttributeAsBool("InheritRedirectDiskDrivesCustom"); + } + else if (_confVersion >= 0.5) + { + // used to be boolean + bool tmpRedirect = xmlnode.GetAttributeAsBool("RedirectDiskDrives"); + if (tmpRedirect) + connectionInfo.RedirectDiskDrives = RDPDiskDrives.Local; + else + connectionInfo.RedirectDiskDrives = RDPDiskDrives.None; + } } catch (Exception ex) { diff --git a/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializer.cs b/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializer.cs index 3fbd5e3da..6b05f91b3 100644 --- a/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializer.cs +++ b/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializer.cs @@ -105,7 +105,7 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers connectionInfo.RedirectSmartCards = value == "1"; break; case "redirectdrives": - connectionInfo.RedirectDiskDrives = value == "1"; + connectionInfo.RedirectDiskDrives = (value == "1" ? RDPDiskDrives.Local : RDPDiskDrives.None); break; case "redirectcomports": connectionInfo.RedirectPorts = value == "1"; diff --git a/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs b/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs index bc427c1cc..45af0013a 100644 --- a/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs +++ b/mRemoteNG/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializer.cs @@ -294,7 +294,7 @@ namespace mRemoteNG.Config.Serializers.MiscSerializers // ./redirectClipboard if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectDrives")?.InnerText, out var redirectDisks)) - connectionInfo.RedirectDiskDrives = redirectDisks; + connectionInfo.RedirectDiskDrives = redirectDisks ? RDPDiskDrives.Local : RDPDiskDrives.None; if (bool.TryParse(localResourcesNode?.SelectSingleNode("./redirectPorts")?.InnerText, out var redirectPorts)) connectionInfo.RedirectPorts = redirectPorts; diff --git a/mRemoteNG/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs b/mRemoteNG/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs index e16a6a4b0..085ff27d9 100644 --- a/mRemoteNG/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs +++ b/mRemoteNG/Config/Serializers/Versioning/SqlDatabaseVersionVerifier.cs @@ -44,6 +44,7 @@ namespace mRemoteNG.Config.Serializers.Versioning new SqlVersion26To27Upgrader(_databaseConnector), new SqlVersion27To28Upgrader(_databaseConnector), new SqlVersion28To29Upgrader(_databaseConnector), + new SqlVersion29To30Upgrader(_databaseConnector), }; foreach (var upgrader in dbUpgraders) diff --git a/mRemoteNG/Config/Serializers/Versioning/SqlVersion29To30Upgrader.cs b/mRemoteNG/Config/Serializers/Versioning/SqlVersion29To30Upgrader.cs new file mode 100644 index 000000000..1606da286 --- /dev/null +++ b/mRemoteNG/Config/Serializers/Versioning/SqlVersion29To30Upgrader.cs @@ -0,0 +1,88 @@ +using mRemoteNG.App; +using mRemoteNG.Config.DatabaseConnectors; +using mRemoteNG.Messages; +using System; +using System.Data.Common; +using System.Runtime.Versioning; + +namespace mRemoteNG.Config.Serializers.Versioning +{ + [SupportedOSPlatform("windows")] + public class SqlVersion29To30Upgrader : IVersionUpgrader + { + private readonly Version version = new Version(3, 0); + private readonly IDatabaseConnector _databaseConnector; + + public SqlVersion29To30Upgrader(IDatabaseConnector databaseConnector) + { + _databaseConnector = databaseConnector ?? throw new ArgumentNullException(nameof(databaseConnector)); + } + + public bool CanUpgrade(Version currentVersion) + { + return currentVersion == new Version(2, 9) || + // Support upgrading during dev revisions, 2.9.1, 2.9.2, etc... + (currentVersion <= new Version(3, 0) && + currentVersion < version); + } + + public Version Upgrade() + { + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, + $"Upgrading database to version {version}."); + + // MYSQL + const string mySqlAlter = @" +ALTER TABLE tblCons ALTER COLUMN `RedirectDiskDrives` varchar(32) DEFAULT NULL; +ALTER TABLE tblCons ADD COLUMN `RedirectDiskDrivesCustom` varchar(32) DEFAULT NULL; +ALTER TABLE tblCons ADD COLUMN `InheritRedirectDiskDrivesCustom` tinyint(1) NOT NULL; +"; + + const string mySqlUpdate = @"UPDATE tblRoot SET ConfVersion=?;"; + + // MS-SQL + const string msSqlAlter = @" +ALTER TABLE tblCons ALTER COLUMN RedirectDiskDrives varchar(32) DEFAULT NULL; +ALTER TABLE tblCons ADD RedirectDiskDrivesCustom varchar(32) DEFAULT NULL; +ALTER TABLE tblCons ADD InheritRedirectDiskDrivesCustom bit NOT NULL; +"; + + const string msSqlUpdate = @"UPDATE tblRoot SET ConfVersion=@confVersion;"; + + using (var sqlTran = _databaseConnector.DbConnection().BeginTransaction(System.Data.IsolationLevel.Serializable)) + { + DbCommand dbCommand; + if (_databaseConnector.GetType() == typeof(MSSqlDatabaseConnector)) + { + dbCommand = _databaseConnector.DbCommand(msSqlAlter); + dbCommand.Transaction = sqlTran; + dbCommand.ExecuteNonQuery(); + dbCommand = _databaseConnector.DbCommand(msSqlUpdate); + dbCommand.Transaction = sqlTran; + } + else if (_databaseConnector.GetType() == typeof(MySqlDatabaseConnector)) + { + dbCommand = _databaseConnector.DbCommand(mySqlAlter); + dbCommand.Transaction = sqlTran; + dbCommand.ExecuteNonQuery(); + dbCommand = _databaseConnector.DbCommand(mySqlUpdate); + dbCommand.Transaction = sqlTran; + } + else + { + throw new Exception("Unknown database back-end"); + } + var pConfVersion = dbCommand.CreateParameter(); + pConfVersion.ParameterName = "confVersion"; + pConfVersion.Value = version.ToString(); + pConfVersion.DbType = System.Data.DbType.String; + pConfVersion.Direction = System.Data.ParameterDirection.Input; + dbCommand.Parameters.Add(pConfVersion); + + dbCommand.ExecuteNonQuery(); + sqlTran.Commit(); + } + return version; + } + } +} \ No newline at end of file diff --git a/mRemoteNG/Connection/AbstractConnectionRecord.cs b/mRemoteNG/Connection/AbstractConnectionRecord.cs index 30d3234c9..3a733539f 100644 --- a/mRemoteNG/Connection/AbstractConnectionRecord.cs +++ b/mRemoteNG/Connection/AbstractConnectionRecord.cs @@ -76,7 +76,8 @@ namespace mRemoteNG.Connection private bool _disableCursorBlinking; private bool _redirectKeys; - private bool _redirectDiskDrives; + private RDPDiskDrives _redirectDiskDrives; + private string _redirectDiskDrivesCustom; private bool _redirectPrinters; private bool _redirectClipboard; private bool _redirectPorts; @@ -713,14 +714,24 @@ namespace mRemoteNG.Connection [LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 6), LocalizedAttributes.LocalizedDisplayName(nameof(Language.DiskDrives)), LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRedirectDrives)), - TypeConverter(typeof(MiscTools.YesNoTypeConverter)), + TypeConverter(typeof(MiscTools.EnumTypeConverter)), AttributeUsedInProtocol(ProtocolType.RDP)] - public bool RedirectDiskDrives + public RDPDiskDrives RedirectDiskDrives { get => GetPropertyValue("RedirectDiskDrives", _redirectDiskDrives); set => SetField(ref _redirectDiskDrives, value, "RedirectDiskDrives"); } + [LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 6), + LocalizedAttributes.LocalizedDisplayName(nameof(Language.RedirectDiskDrivesCustom)), + LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRedirectDiskDrivesCustom)), + AttributeUsedInProtocol(ProtocolType.RDP)] + public string RedirectDiskDrivesCustom + { + get => GetPropertyValue("RedirectDiskDrivesCustom", _redirectDiskDrivesCustom); + set => SetField(ref _redirectDiskDrivesCustom, value, "RedirectDiskDrivesCustom"); + } + [LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 6), LocalizedAttributes.LocalizedDisplayName(nameof(Language.Printers)), LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRedirectPrinters)), diff --git a/mRemoteNG/Connection/ConnectionInfo.cs b/mRemoteNG/Connection/ConnectionInfo.cs index c93fbd39d..33bc5b9de 100644 --- a/mRemoteNG/Connection/ConnectionInfo.cs +++ b/mRemoteNG/Connection/ConnectionInfo.cs @@ -356,7 +356,8 @@ namespace mRemoteNG.Connection private void SetRedirectDefaults() { RedirectKeys = Settings.Default.ConDefaultRedirectKeys; - RedirectDiskDrives = Settings.Default.ConDefaultRedirectDiskDrives; + RedirectDiskDrives = (RDPDiskDrives)Enum.Parse(typeof(RDPDiskDrives), Settings.Default.ConDefaultRedirectDiskDrives); + RedirectDiskDrivesCustom = Settings.Default.ConDefaultRedirectDiskDrivesCustom; RedirectPrinters = Settings.Default.ConDefaultRedirectPrinters; RedirectClipboard = Settings.Default.ConDefaultRedirectClipboard; RedirectPorts = Settings.Default.ConDefaultRedirectPorts; diff --git a/mRemoteNG/Connection/ConnectionInfoInheritance.cs b/mRemoteNG/Connection/ConnectionInfoInheritance.cs index 0ee451cc3..617f28d51 100644 --- a/mRemoteNG/Connection/ConnectionInfoInheritance.cs +++ b/mRemoteNG/Connection/ConnectionInfoInheritance.cs @@ -363,6 +363,12 @@ namespace mRemoteNG.Connection TypeConverter(typeof(MiscTools.YesNoTypeConverter))] public bool RedirectDiskDrives { get; set; } + [LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 7), + LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.RedirectDiskDrivesCustom)), + LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionRedirectDiskDrivesCustom)), + TypeConverter(typeof(MiscTools.YesNoTypeConverter))] + public bool RedirectDiskDrivesCustom { get; set; } + [LocalizedAttributes.LocalizedCategory(nameof(Language.Redirect), 7), LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.Printers)), LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.PropertyDescriptionRedirectPrinters)), diff --git a/mRemoteNG/Connection/Protocol/RDP/RDPDiskDrives.cs b/mRemoteNG/Connection/Protocol/RDP/RDPDiskDrives.cs new file mode 100644 index 000000000..40ae82866 --- /dev/null +++ b/mRemoteNG/Connection/Protocol/RDP/RDPDiskDrives.cs @@ -0,0 +1,20 @@ +using mRemoteNG.Tools; +using mRemoteNG.Resources.Language; + +namespace mRemoteNG.Connection.Protocol.RDP +{ + public enum RDPDiskDrives + { + [LocalizedAttributes.LocalizedDescription(nameof(Language.RdpDrivesNone))] + None, + + [LocalizedAttributes.LocalizedDescription(nameof(Language.RdpDrivesLocal))] + Local, + + [LocalizedAttributes.LocalizedDescription(nameof(Language.RdpDrivesAll))] + All, + + [LocalizedAttributes.LocalizedDescription(nameof(Language.RdpDrivesCustom))] + Custom + } +} \ No newline at end of file diff --git a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs index 3a6b80cf4..144695aa3 100644 --- a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs +++ b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs @@ -1,867 +1,908 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Timers; -using System.Windows.Forms; -using AxMSTSCLib; -using mRemoteNG.App; -using mRemoteNG.Messages; -using mRemoteNG.Properties; -using mRemoteNG.Security.SymmetricEncryption; -using mRemoteNG.Tools; -using mRemoteNG.UI; -using mRemoteNG.UI.Forms; -using mRemoteNG.UI.Tabs; -using MSTSCLib; -using mRemoteNG.Resources.Language; -using System.Runtime.Versioning; -using FileDialog = Microsoft.Win32.FileDialog; - -namespace mRemoteNG.Connection.Protocol.RDP -{ - [SupportedOSPlatform("windows")] - public class RdpProtocol : ProtocolBase, ISupportsViewOnly - { - /* RDP v8 requires Windows 7 with: - * https://support.microsoft.com/en-us/kb/2592687 - * OR - * https://support.microsoft.com/en-us/kb/2923545 - * - * Windows 8+ support RDP v8 out of the box. - */ - - private MsRdpClient6NotSafeForScripting _rdpClient; // lowest version supported - protected virtual RdpVersion RdpProtocolVersion => RDP.RdpVersion.Rdc6; - protected ConnectionInfo connectionInfo; - protected Version RdpVersion; - private readonly DisplayProperties _displayProperties; - protected readonly FrmMain _frmMain = FrmMain.Default; - protected bool loginComplete; - private bool _redirectKeys; - private bool _alertOnIdleDisconnect; - protected uint DesktopScaleFactor => (uint)(_displayProperties.ResolutionScalingFactor.Width * 100); - protected readonly uint DeviceScaleFactor = 100; - protected readonly uint Orientation = 0; - private AxHost AxHost => (AxHost)Control; - - - #region Properties - - public virtual bool SmartSize - { - get => _rdpClient.AdvancedSettings2.SmartSizing; - protected set => _rdpClient.AdvancedSettings2.SmartSizing = value; - } - - public virtual bool Fullscreen - { - get => _rdpClient.FullScreen; - protected set => _rdpClient.FullScreen = value; - } - - private bool RedirectKeys - { - set - { - _redirectKeys = value; - try - { - if (!_redirectKeys) - { - return; - } - - Debug.Assert(Convert.ToBoolean(_rdpClient.SecuredSettingsEnabled)); - IMsRdpClientSecuredSettings msRdpClientSecuredSettings = _rdpClient.SecuredSettings2; - msRdpClientSecuredSettings.KeyboardHookMode = 1; // Apply key combinations at the remote server. - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetRedirectKeysFailed, ex); - } - } - } - - public bool LoadBalanceInfoUseUtf8 { get; set; } - - public bool ViewOnly - { - get => !AxHost.Enabled; - set => AxHost.Enabled = !value; - } - - #endregion - - #region Constructors - - public RdpProtocol() - { - _displayProperties = new DisplayProperties(); - tmrReconnect.Elapsed += tmrReconnect_Elapsed; - } - - #endregion - - #region Public Methods - - protected virtual AxHost CreateActiveXRdpClientControl() - { - return new AxMsRdpClient6NotSafeForScripting(); - } - - public override bool Initialize() - { - connectionInfo = InterfaceControl.Info; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Requesting RDP version: {connectionInfo.RdpVersion}. Using: {RdpProtocolVersion}"); - Control = CreateActiveXRdpClientControl(); - base.Initialize(); - - try - { - if (!InitializeActiveXControl()) return false; - - RdpVersion = new Version(_rdpClient.Version); - - if (RdpVersion < Versions.RDC61) return false; // only RDP versions 6.1 and greater are supported; minimum dll version checked, MSTSCLIB is not capable - - SetRdpClientProperties(); - - return true; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPropsFailed, ex); - return false; - } - } - - private bool InitializeActiveXControl() - { - try - { - Control.GotFocus += RdpClient_GotFocus; - - Control.CreateControl(); - - while (!Control.Created) - { - Thread.Sleep(50); - Application.DoEvents(); - } - Control.Anchor = AnchorStyles.None; - - _rdpClient = (MsRdpClient6NotSafeForScripting)((AxHost)Control).GetOcx(); - - return true; - } - catch (COMException ex) - { - if (ex.Message.Contains("CLASS_E_CLASSNOTAVAILABLE")) - { - Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, string.Format(Language.RdpProtocolVersionNotSupported, connectionInfo.RdpVersion)); - } - else - { - Runtime.MessageCollector.AddExceptionMessage(Language.RdpControlCreationFailed, ex); - } - Control.Dispose(); - return false; - } - } - - public override bool Connect() - { - loginComplete = false; - SetEventHandlers(); - - try - { - _rdpClient.Connect(); - base.Connect(); - - return true; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.ConnectionOpenFailed, ex); - } - - return false; - } - - public override void Disconnect() - { - try - { - _rdpClient.Disconnect(); - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpDisconnectFailed, ex); - Close(); - } - } - - public void ToggleFullscreen() - { - try - { - Fullscreen = !Fullscreen; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpToggleFullscreenFailed, ex); - } - } - - public void ToggleSmartSize() - { - try - { - SmartSize = !SmartSize; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpToggleSmartSizeFailed, ex); - } - } - - /// - /// Toggles whether the RDP ActiveX control will capture and send input events to the remote host. - /// The local host will continue to receive data from the remote host regardless of this setting. - /// - public void ToggleViewOnly() - { - try - { - ViewOnly = !ViewOnly; - } - catch - { - Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg, $"Could not toggle view only mode for host {connectionInfo.Hostname}"); - } - } - - public override void Focus() - { - try - { - if (Control.ContainsFocus == false) - { - Control.Focus(); - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpFocusFailed, ex); - } - } - - /// - /// Determines if this version of the RDP client - /// is supported on this machine. - /// - /// - public bool RdpVersionSupported() - { - try - { - using var control = CreateActiveXRdpClientControl(); - control.CreateControl(); - return true; - } - catch - { - return false; - } - } - #endregion - - #region Private Methods - - protected static class Versions - { - // https://en.wikipedia.org/wiki/Remote_Desktop_Protocol - public static readonly Version RDC60 = new Version(6, 0, 6000); - public static readonly Version RDC61 = new Version(6, 0, 6001); - public static readonly Version RDC70 = new Version(6, 1, 7600); - public static readonly Version RDC80 = new Version(6, 2, 9200); - public static readonly Version RDC81 = new Version(6, 3, 9600); - public static readonly Version RDC100 = new Version(10, 0, 0); - } - - private void SetRdpClientProperties() - { - // https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/rdp-files - - _rdpClient.Server = connectionInfo.Hostname; - - SetCredentials(); - SetResolution(); - _rdpClient.FullScreenTitle = connectionInfo.Name; - - _alertOnIdleDisconnect = connectionInfo.RDPAlertIdleTimeout; - _rdpClient.AdvancedSettings2.MinutesToIdleTimeout = connectionInfo.RDPMinutesToIdleTimeout; - - #region Remote Desktop Services - _rdpClient.SecuredSettings2.StartProgram = connectionInfo.RDPStartProgram; - _rdpClient.SecuredSettings2.WorkDir = connectionInfo.RDPStartProgramWorkDir; - #endregion - - //not user changeable - _rdpClient.AdvancedSettings2.GrabFocusOnConnect = true; - _rdpClient.AdvancedSettings3.EnableAutoReconnect = true; - _rdpClient.AdvancedSettings3.MaxReconnectAttempts = Settings.Default.RdpReconnectionCount; - _rdpClient.AdvancedSettings2.keepAliveInterval = 60000; //in milliseconds (10,000 = 10 seconds) - _rdpClient.AdvancedSettings5.AuthenticationLevel = 0; - _rdpClient.AdvancedSettings2.EncryptionEnabled = 1; - - _rdpClient.AdvancedSettings2.overallConnectionTimeout = Settings.Default.ConRDPOverallConnectionTimeout; - - _rdpClient.AdvancedSettings2.BitmapPeristence = Convert.ToInt32(connectionInfo.CacheBitmaps); - - if (RdpVersion >= Versions.RDC61) - { - _rdpClient.AdvancedSettings7.EnableCredSspSupport = connectionInfo.UseCredSsp; - } - - SetUseConsoleSession(); - SetPort(); - RedirectKeys = connectionInfo.RedirectKeys; - SetRedirection(); - SetAuthenticationLevel(); - SetLoadBalanceInfo(); - SetRdGateway(); - ViewOnly = Force.HasFlag(ConnectionInfo.Force.ViewOnly); - - _rdpClient.ColorDepth = (int)connectionInfo.Colors; - - SetPerformanceFlags(); - - _rdpClient.ConnectingText = Language.Connecting; - } - - protected object GetExtendedProperty(string property) - { - try - { - // ReSharper disable once UseIndexedProperty - return ((IMsRdpExtendedSettings)_rdpClient).get_Property(property); - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionMessage($"Error getting extended RDP property '{property}'", ex, MessageClass.WarningMsg, false); - return null; - } - } - - protected void SetExtendedProperty(string property, object value) - { - try - { - // ReSharper disable once UseIndexedProperty - ((IMsRdpExtendedSettings)_rdpClient).set_Property(property, ref value); - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionMessage($"Error setting extended RDP property '{property}'", ex, MessageClass.WarningMsg, false); - } - } - - private void SetRdGateway() - { - try - { - if (_rdpClient.TransportSettings.GatewayIsSupported == 0) - { - Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.RdpGatewayNotSupported, true); - return; - } - - Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.RdpGatewayIsSupported, true); - - if (connectionInfo.RDGatewayUsageMethod == RDGatewayUsageMethod.Never) return; - - // USE GATEWAY - _rdpClient.TransportSettings.GatewayUsageMethod = (uint)connectionInfo.RDGatewayUsageMethod; - _rdpClient.TransportSettings.GatewayHostname = connectionInfo.RDGatewayHostname; - _rdpClient.TransportSettings.GatewayProfileUsageMethod = 1; // TSC_PROXY_PROFILE_MODE_EXPLICIT - if (connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.SmartCard) - { - _rdpClient.TransportSettings.GatewayCredsSource = 1; // TSC_PROXY_CREDS_MODE_SMARTCARD - } - - if (RdpVersion < Versions.RDC61 || Force.HasFlag(ConnectionInfo.Force.NoCredentials)) return; - - switch (connectionInfo.RDGatewayUseConnectionCredentials) - { - case RDGatewayUseConnectionCredentials.Yes: - _rdpClient.TransportSettings2.GatewayUsername = connectionInfo.Username; - _rdpClient.TransportSettings2.GatewayPassword = connectionInfo.Password; - _rdpClient.TransportSettings2.GatewayDomain = connectionInfo?.Domain; - break; - case RDGatewayUseConnectionCredentials.SmartCard: - _rdpClient.TransportSettings2.GatewayCredSharing = 0; - break; - default: - { - _rdpClient.TransportSettings2.GatewayCredSharing = 0; - - var gwu = connectionInfo.RDGatewayUsername; - var gwp = connectionInfo.RDGatewayPassword; - var gwd = connectionInfo.RDGatewayDomain; - var pkey = ""; - - // access secret server api if necessary - if (InterfaceControl.Info.RDGatewayExternalCredentialProvider == ExternalCredentialProvider.DelineaSecretServer) - { - try - { - string RDGUserViaAPI = InterfaceControl.Info.RDGatewayUserViaAPI; - ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer($"{RDGUserViaAPI}", out gwu, out gwp, out gwd, out pkey); - } - catch (Exception ex) - { - Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); - } - - } - _rdpClient.TransportSettings2.GatewayUsername = gwu; - _rdpClient.TransportSettings2.GatewayPassword = gwp; - _rdpClient.TransportSettings2.GatewayDomain = gwd; - break; - } - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetGatewayFailed, ex); - } - } - - private void SetUseConsoleSession() - { - try - { - bool value; - - if (Force.HasFlag(ConnectionInfo.Force.UseConsoleSession)) - { - value = true; - } - else if (Force.HasFlag(ConnectionInfo.Force.DontUseConsoleSession)) - { - value = false; - } - else - { - value = connectionInfo.UseConsoleSession; - } - - if (RdpVersion >= Versions.RDC61) - { - Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.RdpSetConsoleSwitch, RdpVersion), true); - _rdpClient.AdvancedSettings7.ConnectToAdministerServer = value; - } - else - { - Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, $"{string.Format(Language.RdpSetConsoleSwitch, RdpVersion)}{Environment.NewLine}No longer supported in this RDP version. Reference: https://msdn.microsoft.com/en-us/library/aa380863(v=vs.85).aspx", true); - // ConnectToServerConsole is deprecated - //https://msdn.microsoft.com/en-us/library/aa380863(v=vs.85).aspx - //_rdpClient.AdvancedSettings2.ConnectToServerConsole = value; - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetConsoleSessionFailed, ex); - } - } - - private void SetCredentials() - { - try - { - if (Force.HasFlag(ConnectionInfo.Force.NoCredentials)) - { - return; - } - - var userName = connectionInfo?.Username ?? ""; - var password = connectionInfo?.Password ?? ""; - var domain = connectionInfo?.Domain ?? ""; - var userViaApi = connectionInfo?.UserViaAPI ?? ""; - var pkey = ""; - - // access secret server api if necessary - if (InterfaceControl.Info.ExternalCredentialProvider == ExternalCredentialProvider.DelineaSecretServer) - { - try - { - ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer($"{userViaApi}", out userName, out password, out domain, out pkey); - } - catch (Exception ex) - { - Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); - } - - } - - if (string.IsNullOrEmpty(userName)) - { - switch (Properties.OptionsCredentialsPage.Default.EmptyCredentials) - { - case "windows": - _rdpClient.UserName = Environment.UserName; - break; - case "custom" when !string.IsNullOrEmpty(Properties.OptionsCredentialsPage.Default.DefaultUsername): - _rdpClient.UserName = Properties.OptionsCredentialsPage.Default.DefaultUsername; - break; - case "custom": - try - { - ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer(Properties.OptionsCredentialsPage.Default.UserViaAPIDefault, out userName, out password, out domain, out pkey); - _rdpClient.UserName = userName; - } - catch (Exception ex) - { - Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); - } - - break; - } - } - else - { - _rdpClient.UserName = userName; - } - - if (string.IsNullOrEmpty(password)) - { - if (Properties.OptionsCredentialsPage.Default.EmptyCredentials == "custom") - { - if (Properties.OptionsCredentialsPage.Default.DefaultPassword != "") - { - var cryptographyProvider = new LegacyRijndaelCryptographyProvider(); - _rdpClient.AdvancedSettings2.ClearTextPassword = cryptographyProvider.Decrypt(Properties.OptionsCredentialsPage.Default.DefaultPassword, Runtime.EncryptionKey); - } - } - } - else - { - _rdpClient.AdvancedSettings2.ClearTextPassword = password; - } - - if (string.IsNullOrEmpty(domain)) - { - _rdpClient.Domain = Properties.OptionsCredentialsPage.Default.EmptyCredentials switch - { - "windows" => Environment.UserDomainName, - "custom" => Properties.OptionsCredentialsPage.Default.DefaultDomain, - _ => _rdpClient.Domain - }; - } - else - { - _rdpClient.Domain = domain; - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetCredentialsFailed, ex); - } - } - - private void SetResolution() - { - try - { - SetExtendedProperty("DesktopScaleFactor", DesktopScaleFactor); - SetExtendedProperty("DeviceScaleFactor", DeviceScaleFactor); - - if (Force.HasFlag(ConnectionInfo.Force.Fullscreen)) - { - _rdpClient.FullScreen = true; - _rdpClient.DesktopWidth = Screen.FromControl(_frmMain).Bounds.Width; - _rdpClient.DesktopHeight = Screen.FromControl(_frmMain).Bounds.Height; - - return; - } - - switch (InterfaceControl.Info.Resolution) - { - case RDPResolutions.FitToWindow: - case RDPResolutions.SmartSize: - { - _rdpClient.DesktopWidth = InterfaceControl.Size.Width; - _rdpClient.DesktopHeight = InterfaceControl.Size.Height; - - if (InterfaceControl.Info.Resolution == RDPResolutions.SmartSize) - { - _rdpClient.AdvancedSettings2.SmartSizing = true; - } - - break; - } - case RDPResolutions.Fullscreen: - _rdpClient.FullScreen = true; - _rdpClient.DesktopWidth = Screen.FromControl(_frmMain).Bounds.Width; - _rdpClient.DesktopHeight = Screen.FromControl(_frmMain).Bounds.Height; - break; - default: - { - var resolution = connectionInfo.Resolution.GetResolutionRectangle(); - _rdpClient.DesktopWidth = resolution.Width; - _rdpClient.DesktopHeight = resolution.Height; - break; - } - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetResolutionFailed, ex); - } - } - - private void SetPort() - { - try - { - if (connectionInfo.Port != (int)Defaults.Port) - { - _rdpClient.AdvancedSettings2.RDPPort = connectionInfo.Port; - } - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPortFailed, ex); - } - } - - private void SetRedirection() - { - try - { - _rdpClient.AdvancedSettings2.RedirectDrives = connectionInfo.RedirectDiskDrives; - _rdpClient.AdvancedSettings2.RedirectPorts = connectionInfo.RedirectPorts; - _rdpClient.AdvancedSettings2.RedirectPrinters = connectionInfo.RedirectPrinters; - _rdpClient.AdvancedSettings2.RedirectSmartCards = connectionInfo.RedirectSmartCards; - _rdpClient.SecuredSettings2.AudioRedirectionMode = (int)connectionInfo.RedirectSound; - _rdpClient.AdvancedSettings6.RedirectClipboard = connectionInfo.RedirectClipboard; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetRedirectionFailed, ex); - } - } - - private void SetPerformanceFlags() - { - try - { - var pFlags = 0; - if (connectionInfo.DisplayThemes == false) - pFlags += (int)RDPPerformanceFlags.DisableThemes; - - if (connectionInfo.DisplayWallpaper == false) - pFlags += (int)RDPPerformanceFlags.DisableWallpaper; - - if (connectionInfo.EnableFontSmoothing) - pFlags += (int)RDPPerformanceFlags.EnableFontSmoothing; - - if (connectionInfo.EnableDesktopComposition) - pFlags += (int)RDPPerformanceFlags.EnableDesktopComposition; - - if (connectionInfo.DisableFullWindowDrag) - pFlags += (int)RDPPerformanceFlags.DisableFullWindowDrag; - - if (connectionInfo.DisableMenuAnimations) - pFlags += (int)RDPPerformanceFlags.DisableMenuAnimations; - - if (connectionInfo.DisableCursorShadow) - pFlags += (int)RDPPerformanceFlags.DisableCursorShadow; - - if (connectionInfo.DisableCursorBlinking) - pFlags += (int)RDPPerformanceFlags.DisableCursorBlinking; - - _rdpClient.AdvancedSettings2.PerformanceFlags = pFlags; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPerformanceFlagsFailed, ex); - } - } - - private void SetAuthenticationLevel() - { - try - { - _rdpClient.AdvancedSettings5.AuthenticationLevel = (uint)connectionInfo.RDPAuthenticationLevel; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetAuthenticationLevelFailed, ex); - } - } - - private void SetLoadBalanceInfo() - { - if (string.IsNullOrEmpty(connectionInfo.LoadBalanceInfo)) - { - return; - } - - try - { - _rdpClient.AdvancedSettings2.LoadBalanceInfo = LoadBalanceInfoUseUtf8 - ? new AzureLoadBalanceInfoEncoder().Encode(connectionInfo.LoadBalanceInfo) - : connectionInfo.LoadBalanceInfo; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace("Unable to set load balance info.", ex); - } - } - - protected virtual void SetEventHandlers() - { - try - { - _rdpClient.OnConnecting += RDPEvent_OnConnecting; - _rdpClient.OnConnected += RDPEvent_OnConnected; - _rdpClient.OnLoginComplete += RDPEvent_OnLoginComplete; - _rdpClient.OnFatalError += RDPEvent_OnFatalError; - _rdpClient.OnDisconnected += RDPEvent_OnDisconnected; - _rdpClient.OnIdleTimeoutNotification += RDPEvent_OnIdleTimeoutNotification; - _rdpClient.OnLeaveFullScreenMode += RDPEvent_OnLeaveFullscreenMode; - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetEventHandlersFailed, ex); - } - } - - #endregion - - #region Private Events & Handlers - - private void RDPEvent_OnIdleTimeoutNotification() - { - Close(); //Simply close the RDP Session if the idle timeout has been triggered. - - if (!_alertOnIdleDisconnect) return; - MessageBox.Show($@"The {connectionInfo.Name} session was disconnected due to inactivity", @"Session Disconnected", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - private void RDPEvent_OnFatalError(int errorCode) - { - var errorMsg = RdpErrorCodes.GetError(errorCode); - Event_ErrorOccured(this, errorMsg, errorCode); - } - - private void RDPEvent_OnDisconnected(int discReason) - { - const int UI_ERR_NORMAL_DISCONNECT = 0xB08; - if (discReason != UI_ERR_NORMAL_DISCONNECT) - { - var reason = _rdpClient.GetErrorDescription((uint)discReason, (uint)_rdpClient.ExtendedDisconnectReason); - Event_Disconnected(this, reason, discReason); - } - - if (Properties.OptionsAdvancedPage.Default.ReconnectOnDisconnect) - { - ReconnectGroup = new ReconnectGroup(); - ReconnectGroup.CloseClicked += Event_ReconnectGroupCloseClicked; - ReconnectGroup.Left = (int)((double)Control.Width / 2 - (double)ReconnectGroup.Width / 2); - ReconnectGroup.Top = (int)((double)Control.Height / 2 - (double)ReconnectGroup.Height / 2); - ReconnectGroup.Parent = Control; - ReconnectGroup.Show(); - tmrReconnect.Enabled = true; - } - else - { - Close(); - } - } - - private void RDPEvent_OnConnecting() - { - Event_Connecting(this); - } - - private void RDPEvent_OnConnected() - { - Event_Connected(this); - } - - private void RDPEvent_OnLoginComplete() - { - loginComplete = true; - } - - private void RDPEvent_OnLeaveFullscreenMode() - { - Fullscreen = false; - _leaveFullscreenEvent?.Invoke(this, EventArgs.Empty); - } - - private void RdpClient_GotFocus(object sender, EventArgs e) - { - ((ConnectionTab)Control.Parent.Parent).Focus(); - } - #endregion - - #region Public Events & Handlers - - public delegate void LeaveFullscreenEventHandler(object sender, EventArgs e); - - private LeaveFullscreenEventHandler _leaveFullscreenEvent; - - public event LeaveFullscreenEventHandler LeaveFullscreen - { - add => _leaveFullscreenEvent = (LeaveFullscreenEventHandler)Delegate.Combine(_leaveFullscreenEvent, value); - remove => _leaveFullscreenEvent = (LeaveFullscreenEventHandler)Delegate.Remove(_leaveFullscreenEvent, value); - } - - #endregion - - #region Enums - - public enum Defaults - { - Colors = RDPColors.Colors16Bit, - Sounds = RDPSounds.DoNotPlay, - Resolution = RDPResolutions.FitToWindow, - Port = 3389 - } - - #endregion - - #region Reconnect Stuff - - private void tmrReconnect_Elapsed(object sender, ElapsedEventArgs e) - { - try - { - var srvReady = PortScanner.IsPortOpen(connectionInfo.Hostname, Convert.ToString(connectionInfo.Port)); - - ReconnectGroup.ServerReady = srvReady; - - if (!ReconnectGroup.ReconnectWhenReady || !srvReady) return; - tmrReconnect.Enabled = false; - ReconnectGroup.DisposeReconnectGroup(); - //SetProps() - _rdpClient.Connect(); - } - catch (Exception ex) - { - Runtime.MessageCollector.AddExceptionMessage( - string.Format(Language.AutomaticReconnectError, connectionInfo.Hostname), - ex, MessageClass.WarningMsg, false); - } - } - - #endregion - - } +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Timers; +using System.Windows.Forms; +using AxMSTSCLib; +using mRemoteNG.App; +using mRemoteNG.Messages; +using mRemoteNG.Properties; +using mRemoteNG.Security.SymmetricEncryption; +using mRemoteNG.Tools; +using mRemoteNG.UI; +using mRemoteNG.UI.Forms; +using mRemoteNG.UI.Tabs; +using MSTSCLib; +using mRemoteNG.Resources.Language; +using System.Runtime.Versioning; +using FileDialog = Microsoft.Win32.FileDialog; + +namespace mRemoteNG.Connection.Protocol.RDP +{ + [SupportedOSPlatform("windows")] + public class RdpProtocol : ProtocolBase, ISupportsViewOnly + { + /* RDP v8 requires Windows 7 with: + * https://support.microsoft.com/en-us/kb/2592687 + * OR + * https://support.microsoft.com/en-us/kb/2923545 + * + * Windows 8+ support RDP v8 out of the box. + */ + + private MsRdpClient6NotSafeForScripting _rdpClient; // lowest version supported + protected virtual RdpVersion RdpProtocolVersion => RDP.RdpVersion.Rdc6; + protected ConnectionInfo connectionInfo; + protected Version RdpVersion; + private readonly DisplayProperties _displayProperties; + protected readonly FrmMain _frmMain = FrmMain.Default; + protected bool loginComplete; + private bool _redirectKeys; + private bool _alertOnIdleDisconnect; + protected uint DesktopScaleFactor => (uint)(_displayProperties.ResolutionScalingFactor.Width * 100); + protected readonly uint DeviceScaleFactor = 100; + protected readonly uint Orientation = 0; + private AxHost AxHost => (AxHost)Control; + + + #region Properties + + public virtual bool SmartSize + { + get => _rdpClient.AdvancedSettings2.SmartSizing; + protected set => _rdpClient.AdvancedSettings2.SmartSizing = value; + } + + public virtual bool Fullscreen + { + get => _rdpClient.FullScreen; + protected set => _rdpClient.FullScreen = value; + } + + private bool RedirectKeys + { + set + { + _redirectKeys = value; + try + { + if (!_redirectKeys) + { + return; + } + + Debug.Assert(Convert.ToBoolean(_rdpClient.SecuredSettingsEnabled)); + IMsRdpClientSecuredSettings msRdpClientSecuredSettings = _rdpClient.SecuredSettings2; + msRdpClientSecuredSettings.KeyboardHookMode = 1; // Apply key combinations at the remote server. + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetRedirectKeysFailed, ex); + } + } + } + + public bool LoadBalanceInfoUseUtf8 { get; set; } + + public bool ViewOnly + { + get => !AxHost.Enabled; + set => AxHost.Enabled = !value; + } + + #endregion + + #region Constructors + + public RdpProtocol() + { + _displayProperties = new DisplayProperties(); + tmrReconnect.Elapsed += tmrReconnect_Elapsed; + } + + #endregion + + #region Public Methods + + protected virtual AxHost CreateActiveXRdpClientControl() + { + return new AxMsRdpClient6NotSafeForScripting(); + } + + public override bool Initialize() + { + connectionInfo = InterfaceControl.Info; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Requesting RDP version: {connectionInfo.RdpVersion}. Using: {RdpProtocolVersion}"); + Control = CreateActiveXRdpClientControl(); + base.Initialize(); + + try + { + if (!InitializeActiveXControl()) return false; + + RdpVersion = new Version(_rdpClient.Version); + + if (RdpVersion < Versions.RDC61) return false; // only RDP versions 6.1 and greater are supported; minimum dll version checked, MSTSCLIB is not capable + + SetRdpClientProperties(); + + return true; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPropsFailed, ex); + return false; + } + } + + private bool InitializeActiveXControl() + { + try + { + Control.GotFocus += RdpClient_GotFocus; + + Control.CreateControl(); + + while (!Control.Created) + { + Thread.Sleep(50); + Application.DoEvents(); + } + Control.Anchor = AnchorStyles.None; + + _rdpClient = (MsRdpClient6NotSafeForScripting)((AxHost)Control).GetOcx(); + + return true; + } + catch (COMException ex) + { + if (ex.Message.Contains("CLASS_E_CLASSNOTAVAILABLE")) + { + Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg, string.Format(Language.RdpProtocolVersionNotSupported, connectionInfo.RdpVersion)); + } + else + { + Runtime.MessageCollector.AddExceptionMessage(Language.RdpControlCreationFailed, ex); + } + Control.Dispose(); + return false; + } + } + + public override bool Connect() + { + loginComplete = false; + SetEventHandlers(); + + try + { + _rdpClient.Connect(); + base.Connect(); + + return true; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.ConnectionOpenFailed, ex); + } + + return false; + } + + public override void Disconnect() + { + try + { + _rdpClient.Disconnect(); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpDisconnectFailed, ex); + Close(); + } + } + + public void ToggleFullscreen() + { + try + { + Fullscreen = !Fullscreen; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpToggleFullscreenFailed, ex); + } + } + + public void ToggleSmartSize() + { + try + { + SmartSize = !SmartSize; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpToggleSmartSizeFailed, ex); + } + } + + /// + /// Toggles whether the RDP ActiveX control will capture and send input events to the remote host. + /// The local host will continue to receive data from the remote host regardless of this setting. + /// + public void ToggleViewOnly() + { + try + { + ViewOnly = !ViewOnly; + } + catch + { + Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg, $"Could not toggle view only mode for host {connectionInfo.Hostname}"); + } + } + + public override void Focus() + { + try + { + if (Control.ContainsFocus == false) + { + Control.Focus(); + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpFocusFailed, ex); + } + } + + /// + /// Determines if this version of the RDP client + /// is supported on this machine. + /// + /// + public bool RdpVersionSupported() + { + try + { + using var control = CreateActiveXRdpClientControl(); + control.CreateControl(); + return true; + } + catch + { + return false; + } + } + #endregion + + #region Private Methods + + protected static class Versions + { + // https://en.wikipedia.org/wiki/Remote_Desktop_Protocol + public static readonly Version RDC60 = new Version(6, 0, 6000); + public static readonly Version RDC61 = new Version(6, 0, 6001); + public static readonly Version RDC70 = new Version(6, 1, 7600); + public static readonly Version RDC80 = new Version(6, 2, 9200); + public static readonly Version RDC81 = new Version(6, 3, 9600); + public static readonly Version RDC100 = new Version(10, 0, 0); + } + + private void SetRdpClientProperties() + { + // https://learn.microsoft.com/en-us/windows-server/remote/remote-desktop-services/clients/rdp-files + + _rdpClient.Server = connectionInfo.Hostname; + + SetCredentials(); + SetResolution(); + _rdpClient.FullScreenTitle = connectionInfo.Name; + + _alertOnIdleDisconnect = connectionInfo.RDPAlertIdleTimeout; + _rdpClient.AdvancedSettings2.MinutesToIdleTimeout = connectionInfo.RDPMinutesToIdleTimeout; + + #region Remote Desktop Services + _rdpClient.SecuredSettings2.StartProgram = connectionInfo.RDPStartProgram; + _rdpClient.SecuredSettings2.WorkDir = connectionInfo.RDPStartProgramWorkDir; + #endregion + + //not user changeable + _rdpClient.AdvancedSettings2.GrabFocusOnConnect = true; + _rdpClient.AdvancedSettings3.EnableAutoReconnect = true; + _rdpClient.AdvancedSettings3.MaxReconnectAttempts = Settings.Default.RdpReconnectionCount; + _rdpClient.AdvancedSettings2.keepAliveInterval = 60000; //in milliseconds (10,000 = 10 seconds) + _rdpClient.AdvancedSettings5.AuthenticationLevel = 0; + _rdpClient.AdvancedSettings2.EncryptionEnabled = 1; + + _rdpClient.AdvancedSettings2.overallConnectionTimeout = Settings.Default.ConRDPOverallConnectionTimeout; + + _rdpClient.AdvancedSettings2.BitmapPeristence = Convert.ToInt32(connectionInfo.CacheBitmaps); + + if (RdpVersion >= Versions.RDC61) + { + _rdpClient.AdvancedSettings7.EnableCredSspSupport = connectionInfo.UseCredSsp; + } + + SetUseConsoleSession(); + SetPort(); + RedirectKeys = connectionInfo.RedirectKeys; + SetRedirection(); + SetAuthenticationLevel(); + SetLoadBalanceInfo(); + SetRdGateway(); + ViewOnly = Force.HasFlag(ConnectionInfo.Force.ViewOnly); + + _rdpClient.ColorDepth = (int)connectionInfo.Colors; + + SetPerformanceFlags(); + + _rdpClient.ConnectingText = Language.Connecting; + } + + protected object GetExtendedProperty(string property) + { + try + { + // ReSharper disable once UseIndexedProperty + return ((IMsRdpExtendedSettings)_rdpClient).get_Property(property); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage($"Error getting extended RDP property '{property}'", ex, MessageClass.WarningMsg, false); + return null; + } + } + + protected void SetExtendedProperty(string property, object value) + { + try + { + // ReSharper disable once UseIndexedProperty + ((IMsRdpExtendedSettings)_rdpClient).set_Property(property, ref value); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage($"Error setting extended RDP property '{property}'", ex, MessageClass.WarningMsg, false); + } + } + + private void SetRdGateway() + { + try + { + if (_rdpClient.TransportSettings.GatewayIsSupported == 0) + { + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.RdpGatewayNotSupported, true); + return; + } + + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.RdpGatewayIsSupported, true); + + if (connectionInfo.RDGatewayUsageMethod == RDGatewayUsageMethod.Never) return; + + // USE GATEWAY + _rdpClient.TransportSettings.GatewayUsageMethod = (uint)connectionInfo.RDGatewayUsageMethod; + _rdpClient.TransportSettings.GatewayHostname = connectionInfo.RDGatewayHostname; + _rdpClient.TransportSettings.GatewayProfileUsageMethod = 1; // TSC_PROXY_PROFILE_MODE_EXPLICIT + if (connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.SmartCard) + { + _rdpClient.TransportSettings.GatewayCredsSource = 1; // TSC_PROXY_CREDS_MODE_SMARTCARD + } + + if (RdpVersion < Versions.RDC61 || Force.HasFlag(ConnectionInfo.Force.NoCredentials)) return; + + switch (connectionInfo.RDGatewayUseConnectionCredentials) + { + case RDGatewayUseConnectionCredentials.Yes: + _rdpClient.TransportSettings2.GatewayUsername = connectionInfo.Username; + _rdpClient.TransportSettings2.GatewayPassword = connectionInfo.Password; + _rdpClient.TransportSettings2.GatewayDomain = connectionInfo?.Domain; + break; + case RDGatewayUseConnectionCredentials.SmartCard: + _rdpClient.TransportSettings2.GatewayCredSharing = 0; + break; + default: + { + _rdpClient.TransportSettings2.GatewayCredSharing = 0; + + var gwu = connectionInfo.RDGatewayUsername; + var gwp = connectionInfo.RDGatewayPassword; + var gwd = connectionInfo.RDGatewayDomain; + var pkey = ""; + + // access secret server api if necessary + if (InterfaceControl.Info.RDGatewayExternalCredentialProvider == ExternalCredentialProvider.DelineaSecretServer) + { + try + { + string RDGUserViaAPI = InterfaceControl.Info.RDGatewayUserViaAPI; + ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer($"{RDGUserViaAPI}", out gwu, out gwp, out gwd, out pkey); + } + catch (Exception ex) + { + Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); + } + + } + _rdpClient.TransportSettings2.GatewayUsername = gwu; + _rdpClient.TransportSettings2.GatewayPassword = gwp; + _rdpClient.TransportSettings2.GatewayDomain = gwd; + break; + } + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetGatewayFailed, ex); + } + } + + private void SetUseConsoleSession() + { + try + { + bool value; + + if (Force.HasFlag(ConnectionInfo.Force.UseConsoleSession)) + { + value = true; + } + else if (Force.HasFlag(ConnectionInfo.Force.DontUseConsoleSession)) + { + value = false; + } + else + { + value = connectionInfo.UseConsoleSession; + } + + if (RdpVersion >= Versions.RDC61) + { + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, string.Format(Language.RdpSetConsoleSwitch, RdpVersion), true); + _rdpClient.AdvancedSettings7.ConnectToAdministerServer = value; + } + else + { + Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, $"{string.Format(Language.RdpSetConsoleSwitch, RdpVersion)}{Environment.NewLine}No longer supported in this RDP version. Reference: https://msdn.microsoft.com/en-us/library/aa380863(v=vs.85).aspx", true); + // ConnectToServerConsole is deprecated + //https://msdn.microsoft.com/en-us/library/aa380863(v=vs.85).aspx + //_rdpClient.AdvancedSettings2.ConnectToServerConsole = value; + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetConsoleSessionFailed, ex); + } + } + + private void SetCredentials() + { + try + { + if (Force.HasFlag(ConnectionInfo.Force.NoCredentials)) + { + return; + } + + var userName = connectionInfo?.Username ?? ""; + var password = connectionInfo?.Password ?? ""; + var domain = connectionInfo?.Domain ?? ""; + var userViaApi = connectionInfo?.UserViaAPI ?? ""; + var pkey = ""; + + // access secret server api if necessary + if (InterfaceControl.Info.ExternalCredentialProvider == ExternalCredentialProvider.DelineaSecretServer) + { + try + { + ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer($"{userViaApi}", out userName, out password, out domain, out pkey); + } + catch (Exception ex) + { + Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); + } + + } + + if (string.IsNullOrEmpty(userName)) + { + switch (Properties.OptionsCredentialsPage.Default.EmptyCredentials) + { + case "windows": + _rdpClient.UserName = Environment.UserName; + break; + case "custom" when !string.IsNullOrEmpty(Properties.OptionsCredentialsPage.Default.DefaultUsername): + _rdpClient.UserName = Properties.OptionsCredentialsPage.Default.DefaultUsername; + break; + case "custom": + try + { + ExternalConnectors.DSS.SecretServerInterface.FetchSecretFromServer(Properties.OptionsCredentialsPage.Default.UserViaAPIDefault, out userName, out password, out domain, out pkey); + _rdpClient.UserName = userName; + } + catch (Exception ex) + { + Event_ErrorOccured(this, "Secret Server Interface Error: " + ex.Message, 0); + } + + break; + } + } + else + { + _rdpClient.UserName = userName; + } + + if (string.IsNullOrEmpty(password)) + { + if (Properties.OptionsCredentialsPage.Default.EmptyCredentials == "custom") + { + if (Properties.OptionsCredentialsPage.Default.DefaultPassword != "") + { + var cryptographyProvider = new LegacyRijndaelCryptographyProvider(); + _rdpClient.AdvancedSettings2.ClearTextPassword = cryptographyProvider.Decrypt(Properties.OptionsCredentialsPage.Default.DefaultPassword, Runtime.EncryptionKey); + } + } + } + else + { + _rdpClient.AdvancedSettings2.ClearTextPassword = password; + } + + if (string.IsNullOrEmpty(domain)) + { + _rdpClient.Domain = Properties.OptionsCredentialsPage.Default.EmptyCredentials switch + { + "windows" => Environment.UserDomainName, + "custom" => Properties.OptionsCredentialsPage.Default.DefaultDomain, + _ => _rdpClient.Domain + }; + } + else + { + _rdpClient.Domain = domain; + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetCredentialsFailed, ex); + } + } + + private void SetResolution() + { + try + { + SetExtendedProperty("DesktopScaleFactor", DesktopScaleFactor); + SetExtendedProperty("DeviceScaleFactor", DeviceScaleFactor); + + if (Force.HasFlag(ConnectionInfo.Force.Fullscreen)) + { + _rdpClient.FullScreen = true; + _rdpClient.DesktopWidth = Screen.FromControl(_frmMain).Bounds.Width; + _rdpClient.DesktopHeight = Screen.FromControl(_frmMain).Bounds.Height; + + return; + } + + switch (InterfaceControl.Info.Resolution) + { + case RDPResolutions.FitToWindow: + case RDPResolutions.SmartSize: + { + _rdpClient.DesktopWidth = InterfaceControl.Size.Width; + _rdpClient.DesktopHeight = InterfaceControl.Size.Height; + + if (InterfaceControl.Info.Resolution == RDPResolutions.SmartSize) + { + _rdpClient.AdvancedSettings2.SmartSizing = true; + } + + break; + } + case RDPResolutions.Fullscreen: + _rdpClient.FullScreen = true; + _rdpClient.DesktopWidth = Screen.FromControl(_frmMain).Bounds.Width; + _rdpClient.DesktopHeight = Screen.FromControl(_frmMain).Bounds.Height; + break; + default: + { + var resolution = connectionInfo.Resolution.GetResolutionRectangle(); + _rdpClient.DesktopWidth = resolution.Width; + _rdpClient.DesktopHeight = resolution.Height; + break; + } + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetResolutionFailed, ex); + } + } + + private void SetPort() + { + try + { + if (connectionInfo.Port != (int)Defaults.Port) + { + _rdpClient.AdvancedSettings2.RDPPort = connectionInfo.Port; + } + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPortFailed, ex); + } + } + + private void SetRedirection() + { + try + { + SetDriveRedirection(); + _rdpClient.AdvancedSettings2.RedirectPorts = connectionInfo.RedirectPorts; + _rdpClient.AdvancedSettings2.RedirectPrinters = connectionInfo.RedirectPrinters; + _rdpClient.AdvancedSettings2.RedirectSmartCards = connectionInfo.RedirectSmartCards; + _rdpClient.SecuredSettings2.AudioRedirectionMode = (int)connectionInfo.RedirectSound; + _rdpClient.AdvancedSettings6.RedirectClipboard = connectionInfo.RedirectClipboard; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetRedirectionFailed, ex); + } + } + + private void SetDriveRedirection() + { + if (RDPDiskDrives.None == connectionInfo.RedirectDiskDrives) + _rdpClient.AdvancedSettings2.RedirectDrives = false; + else if (RDPDiskDrives.All == connectionInfo.RedirectDiskDrives) + _rdpClient.AdvancedSettings2.RedirectDrives = true; + else if (RDPDiskDrives.Custom == connectionInfo.RedirectDiskDrives) + { + var rdpNS5 = (IMsRdpClientNonScriptable5)((AxHost)Control).GetOcx(); + for (uint i = 0; i < rdpNS5.DriveCollection.DriveCount; i++) + { + IMsRdpDrive drive = rdpNS5.DriveCollection.DriveByIndex[i]; + drive.RedirectionState = connectionInfo.RedirectDiskDrivesCustom.Contains(drive.Name.Substring(0, 1)); + } + } + else + { + // Local Drives + var rdpNS5 = (IMsRdpClientNonScriptable5)((AxHost)Control).GetOcx(); + for (uint i = 0; i < rdpNS5.DriveCollection.DriveCount; i++) + { + IMsRdpDrive drive = rdpNS5.DriveCollection.DriveByIndex[i]; + drive.RedirectionState = IsLocal(drive); + } + } + } + + private bool IsLocal(IMsRdpDrive drive) + { + DriveInfo[] myDrives = DriveInfo.GetDrives(); + foreach (DriveInfo myDrive in myDrives) + { + if (myDrive.Name.Substring(0, 1).Equals(drive.Name.Substring(0,1))) + { + return myDrive.DriveType == DriveType.Fixed; + } + } + return false; + } + + private void SetPerformanceFlags() + { + try + { + var pFlags = 0; + if (connectionInfo.DisplayThemes == false) + pFlags += (int)RDPPerformanceFlags.DisableThemes; + + if (connectionInfo.DisplayWallpaper == false) + pFlags += (int)RDPPerformanceFlags.DisableWallpaper; + + if (connectionInfo.EnableFontSmoothing) + pFlags += (int)RDPPerformanceFlags.EnableFontSmoothing; + + if (connectionInfo.EnableDesktopComposition) + pFlags += (int)RDPPerformanceFlags.EnableDesktopComposition; + + if (connectionInfo.DisableFullWindowDrag) + pFlags += (int)RDPPerformanceFlags.DisableFullWindowDrag; + + if (connectionInfo.DisableMenuAnimations) + pFlags += (int)RDPPerformanceFlags.DisableMenuAnimations; + + if (connectionInfo.DisableCursorShadow) + pFlags += (int)RDPPerformanceFlags.DisableCursorShadow; + + if (connectionInfo.DisableCursorBlinking) + pFlags += (int)RDPPerformanceFlags.DisableCursorBlinking; + + _rdpClient.AdvancedSettings2.PerformanceFlags = pFlags; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetPerformanceFlagsFailed, ex); + } + } + + private void SetAuthenticationLevel() + { + try + { + _rdpClient.AdvancedSettings5.AuthenticationLevel = (uint)connectionInfo.RDPAuthenticationLevel; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetAuthenticationLevelFailed, ex); + } + } + + private void SetLoadBalanceInfo() + { + if (string.IsNullOrEmpty(connectionInfo.LoadBalanceInfo)) + { + return; + } + + try + { + _rdpClient.AdvancedSettings2.LoadBalanceInfo = LoadBalanceInfoUseUtf8 + ? new AzureLoadBalanceInfoEncoder().Encode(connectionInfo.LoadBalanceInfo) + : connectionInfo.LoadBalanceInfo; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace("Unable to set load balance info.", ex); + } + } + + protected virtual void SetEventHandlers() + { + try + { + _rdpClient.OnConnecting += RDPEvent_OnConnecting; + _rdpClient.OnConnected += RDPEvent_OnConnected; + _rdpClient.OnLoginComplete += RDPEvent_OnLoginComplete; + _rdpClient.OnFatalError += RDPEvent_OnFatalError; + _rdpClient.OnDisconnected += RDPEvent_OnDisconnected; + _rdpClient.OnIdleTimeoutNotification += RDPEvent_OnIdleTimeoutNotification; + _rdpClient.OnLeaveFullScreenMode += RDPEvent_OnLeaveFullscreenMode; + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionStackTrace(Language.RdpSetEventHandlersFailed, ex); + } + } + + #endregion + + #region Private Events & Handlers + + private void RDPEvent_OnIdleTimeoutNotification() + { + Close(); //Simply close the RDP Session if the idle timeout has been triggered. + + if (!_alertOnIdleDisconnect) return; + MessageBox.Show($@"The {connectionInfo.Name} session was disconnected due to inactivity", @"Session Disconnected", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + private void RDPEvent_OnFatalError(int errorCode) + { + var errorMsg = RdpErrorCodes.GetError(errorCode); + Event_ErrorOccured(this, errorMsg, errorCode); + } + + private void RDPEvent_OnDisconnected(int discReason) + { + const int UI_ERR_NORMAL_DISCONNECT = 0xB08; + if (discReason != UI_ERR_NORMAL_DISCONNECT) + { + var reason = _rdpClient.GetErrorDescription((uint)discReason, (uint)_rdpClient.ExtendedDisconnectReason); + Event_Disconnected(this, reason, discReason); + } + + if (Properties.OptionsAdvancedPage.Default.ReconnectOnDisconnect) + { + ReconnectGroup = new ReconnectGroup(); + ReconnectGroup.CloseClicked += Event_ReconnectGroupCloseClicked; + ReconnectGroup.Left = (int)((double)Control.Width / 2 - (double)ReconnectGroup.Width / 2); + ReconnectGroup.Top = (int)((double)Control.Height / 2 - (double)ReconnectGroup.Height / 2); + ReconnectGroup.Parent = Control; + ReconnectGroup.Show(); + tmrReconnect.Enabled = true; + } + else + { + Close(); + } + } + + private void RDPEvent_OnConnecting() + { + Event_Connecting(this); + } + + private void RDPEvent_OnConnected() + { + Event_Connected(this); + } + + private void RDPEvent_OnLoginComplete() + { + loginComplete = true; + } + + private void RDPEvent_OnLeaveFullscreenMode() + { + Fullscreen = false; + _leaveFullscreenEvent?.Invoke(this, EventArgs.Empty); + } + + private void RdpClient_GotFocus(object sender, EventArgs e) + { + ((ConnectionTab)Control.Parent.Parent).Focus(); + } + #endregion + + #region Public Events & Handlers + + public delegate void LeaveFullscreenEventHandler(object sender, EventArgs e); + + private LeaveFullscreenEventHandler _leaveFullscreenEvent; + + public event LeaveFullscreenEventHandler LeaveFullscreen + { + add => _leaveFullscreenEvent = (LeaveFullscreenEventHandler)Delegate.Combine(_leaveFullscreenEvent, value); + remove => _leaveFullscreenEvent = (LeaveFullscreenEventHandler)Delegate.Remove(_leaveFullscreenEvent, value); + } + + #endregion + + #region Enums + + public enum Defaults + { + Colors = RDPColors.Colors16Bit, + Sounds = RDPSounds.DoNotPlay, + Resolution = RDPResolutions.FitToWindow, + Port = 3389 + } + + #endregion + + #region Reconnect Stuff + + private void tmrReconnect_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + var srvReady = PortScanner.IsPortOpen(connectionInfo.Hostname, Convert.ToString(connectionInfo.Port)); + + ReconnectGroup.ServerReady = srvReady; + + if (!ReconnectGroup.ReconnectWhenReady || !srvReady) return; + tmrReconnect.Enabled = false; + ReconnectGroup.DisposeReconnectGroup(); + //SetProps() + _rdpClient.Connect(); + } + catch (Exception ex) + { + Runtime.MessageCollector.AddExceptionMessage( + string.Format(Language.AutomaticReconnectError, connectionInfo.Hostname), + ex, MessageClass.WarningMsg, false); + } + } + + #endregion + + } } \ No newline at end of file diff --git a/mRemoteNG/Language/Language.Designer.cs b/mRemoteNG/Language/Language.Designer.cs index 512419a1a..8733b2f01 100644 --- a/mRemoteNG/Language/Language.Designer.cs +++ b/mRemoteNG/Language/Language.Designer.cs @@ -4126,6 +4126,15 @@ namespace mRemoteNG.Resources.Language { } /// + /// Looks up a localized string similar to Enter custom drives to redirect: C,D,X. + /// + internal static string PropertyDescriptionRedirectDiskDrivesCustom { + get { + return ResourceManager.GetString("PropertyDescriptionRedirectDiskDrivesCustom", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Select whether local disk drives should be shown on the remote host.. /// internal static string PropertyDescriptionRedirectDrives { @@ -4756,6 +4765,42 @@ namespace mRemoteNG.Resources.Language { } } + /// + /// Looks up a localized string similar to All. + /// + internal static string RdpDrivesAll { + get { + return ResourceManager.GetString("RdpDrivesAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom. + /// + internal static string RdpDrivesCustom { + get { + return ResourceManager.GetString("RdpDrivesCustom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Local. + /// + internal static string RdpDrivesLocal { + get { + return ResourceManager.GetString("RdpDrivesLocal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to None. + /// + internal static string RdpDrivesNone { + get { + return ResourceManager.GetString("RdpDrivesNone", resourceCulture); + } + } + /// /// Looks up a localized string similar to Internal error code 1.. /// @@ -5179,6 +5224,15 @@ namespace mRemoteNG.Resources.Language { } } + /// + /// Looks up a localized string similar to Custom Drives. + /// + internal static string RedirectDiskDrivesCustom { + get { + return ResourceManager.GetString("RedirectDiskDrivesCustom", resourceCulture); + } + } + /// /// Looks up a localized string similar to Disk Drives. /// diff --git a/mRemoteNG/Language/Language.resx b/mRemoteNG/Language/Language.resx index 55d6dd043..6e9c76e84 100644 --- a/mRemoteNG/Language/Language.resx +++ b/mRemoteNG/Language/Language.resx @@ -2316,4 +2316,22 @@ Nightly Channel includes Alphas, Betas & Release Candidates. Automatically try to reconnect when disconnected from server (RDP && ICA only) + + Enter custom drives to redirect: C,D,X + + + All + + + Custom + + + Local + + + None + + + Custom Drives + \ No newline at end of file diff --git a/mRemoteNG/Properties/Settings.Designer.cs b/mRemoteNG/Properties/Settings.Designer.cs index 272e6d860..2f7241eb4 100644 --- a/mRemoteNG/Properties/Settings.Designer.cs +++ b/mRemoteNG/Properties/Settings.Designer.cs @@ -169,16 +169,31 @@ namespace mRemoteNG.Properties { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool ConDefaultRedirectDiskDrives { + [global::System.Configuration.DefaultSettingValueAttribute("Local")] + public string ConDefaultRedirectDiskDrives { get { - return ((bool)(this["ConDefaultRedirectDiskDrives"])); + return ((string)(this["ConDefaultRedirectDiskDrives"])); } set { this["ConDefaultRedirectDiskDrives"] = value; } } - + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ConDefaultRedirectDiskDrivesCustom + { + get + { + return ((string)(this["ConDefaultRedirectDiskDrivesCustom"])); + } + set + { + this["ConDefaultRedirectDiskDrivesCustom"] = value; + } + } + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] diff --git a/mRemoteNG/Schemas/mremoteng_confcons_v2_8.xsd b/mRemoteNG/Schemas/mremoteng_confcons_v2_8.xsd new file mode 100644 index 000000000..0b8a9ece3 --- /dev/null +++ b/mRemoteNG/Schemas/mremoteng_confcons_v2_8.xsd @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs b/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs index 0b86819c1..3757bf14d 100644 --- a/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs +++ b/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs @@ -290,6 +290,11 @@ namespace mRemoteNG.UI.Controls.ConnectionInfoPropertyGrid strHide.Add(nameof(AbstractConnectionRecord.AutomaticResize)); } + if (SelectedConnectionInfo.RedirectDiskDrives != RDPDiskDrives.Custom) + { + strHide.Add(nameof(AbstractConnectionRecord.RedirectDiskDrivesCustom)); + } + if (SelectedConnectionInfo.RedirectSound != RDPSounds.BringToThisComputer) { strHide.Add(nameof(AbstractConnectionRecord.SoundQuality)); diff --git a/mRemoteNG/mRemoteNG.csproj b/mRemoteNG/mRemoteNG.csproj index 692bf5c63..65eb2648b 100644 --- a/mRemoteNG/mRemoteNG.csproj +++ b/mRemoteNG/mRemoteNG.csproj @@ -447,6 +447,9 @@ Always + + Always + Always diff --git a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializerTests.cs index f5731a3e4..ef62c6e57 100644 --- a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionDeserializerTests.cs @@ -27,7 +27,7 @@ namespace mRemoteNGTests.Config.Serializers.MiscSerializers private const bool ExpectedFontSmoothing = true; private const bool ExpectedDesktopComposition = true; private const bool ExpectedSmartcardRedirection = true; - private const bool ExpectedDriveRedirection = true; + private const RDPDiskDrives ExpectedDriveRedirection = RDPDiskDrives.Local; private const bool ExpectedPortRedirection = true; private const bool ExpectedPrinterRedirection = true; private const RDPSounds ExpectedSoundRedirection = RDPSounds.BringToThisComputer; diff --git a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManager27DeserializerTests.cs b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManager27DeserializerTests.cs index 62b1ff977..d532df98c 100644 --- a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManager27DeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManager27DeserializerTests.cs @@ -35,7 +35,7 @@ namespace mRemoteNGTests.Config.Serializers.MiscSerializers private const RDPSounds ExpectedAudioRedirection = RDPSounds.DoNotPlay; private const bool ExpectedKeyRedirection = true; private const bool ExpectedSmartcardRedirection = true; - private const bool ExpectedDriveRedirection = true; + private const RDPDiskDrives ExpectedDriveRedirection = RDPDiskDrives.Local; private const bool ExpectedPortRedirection = true; private const bool ExpectedPrinterRedirection = true; private const AuthenticationLevel ExpectedAuthLevel = AuthenticationLevel.WarnOnFailedAuth; diff --git a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializerTests.cs b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializerTests.cs index dd2362f65..7cdb07d5d 100644 --- a/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializerTests.cs +++ b/mRemoteNGTests/Config/Serializers/MiscSerializers/RemoteDesktopConnectionManagerDeserializerTests.cs @@ -33,7 +33,7 @@ namespace mRemoteNGTests.Config.Serializers.MiscSerializers private const RDPSounds ExpectedAudioRedirection = RDPSounds.DoNotPlay; private const bool ExpectedKeyRedirection = true; private const bool ExpectedSmartcardRedirection = true; - private const bool ExpectedDriveRedirection = true; + private const RDPDiskDrives ExpectedDriveRedirection = RDPDiskDrives.Local; private const bool ExpectedPortRedirection = true; private const bool ExpectedPrinterRedirection = true; private const AuthenticationLevel ExpectedAuthLevel = AuthenticationLevel.AuthRequired; diff --git a/mRemoteNGTests/Connection/AbstractConnectionInfoDataTests.cs b/mRemoteNGTests/Connection/AbstractConnectionInfoDataTests.cs index 7c816cce3..29c5850f4 100644 --- a/mRemoteNGTests/Connection/AbstractConnectionInfoDataTests.cs +++ b/mRemoteNGTests/Connection/AbstractConnectionInfoDataTests.cs @@ -344,7 +344,7 @@ namespace mRemoteNGTests.Connection { var wasCalled = false; _testAbstractConnectionInfoData.PropertyChanged += (sender, args) => wasCalled = true; - _testAbstractConnectionInfoData.RedirectDiskDrives = true; + _testAbstractConnectionInfoData.RedirectDiskDrives = RDPDiskDrives.Local; Assert.That(wasCalled, Is.True); }