PowerShell/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteSessionCapability.cs
2020-08-31 10:38:05 -07:00

462 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.IO;
using System.Management.Automation.Host;
using System.Management.Automation.Internal.Host;
using System.Runtime.Serialization.Formatters.Binary;
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation.Remoting
{
/// <summary>
/// This class contains information about the capability of one side of the connection. The client
/// side and the server side will have their own capabilities. These two sets of capabilities will
/// be used in a capability negotiation algorithm to determine if it is possible to establish a
/// connection between the client and the server.
/// </summary>
internal class RemoteSessionCapability
{
#region DO NOT REMOVE OR RENAME THESE FIELDS - it will break remoting compatibility with Windows PowerShell
private Version _psversion;
private Version _serversion;
private Version _protocolVersion;
private RemotingDestination _remotingDestination;
private static byte[] _timeZoneInByteFormat;
private TimeZoneInfo _timeZone;
#endregion
internal Version ProtocolVersion
{
get
{
return _protocolVersion;
}
set
{
_protocolVersion = value;
}
}
internal Version PSVersion { get { return _psversion; } }
internal Version SerializationVersion { get { return _serversion; } }
internal RemotingDestination RemotingDestination { get { return _remotingDestination; } }
/// <summary>
/// Constructor for RemoteSessionCapability.
/// </summary>
/// <remarks>should not be called from outside, use create methods instead
/// </remarks>
internal RemoteSessionCapability(RemotingDestination remotingDestination)
{
_protocolVersion = RemotingConstants.ProtocolVersion;
// PS Version 3 is fully backward compatible with Version 2
// In the remoting protocol sense, nothing is changing between PS3 and PS2
// For negotiation to succeed with old client/servers we have to use 2.
_psversion = new Version(2, 0); // PSVersionInfo.PSVersion;
_serversion = PSVersionInfo.SerializationVersion;
_remotingDestination = remotingDestination;
}
internal RemoteSessionCapability(RemotingDestination remotingDestination,
Version protocolVersion,
Version psVersion,
Version serVersion)
{
_protocolVersion = protocolVersion;
_psversion = psVersion;
_serversion = serVersion;
_remotingDestination = remotingDestination;
}
/// <summary>
/// Create client capability.
/// </summary>
internal static RemoteSessionCapability CreateClientCapability()
{
return new RemoteSessionCapability(RemotingDestination.Server);
}
/// <summary>
/// Create server capability.
/// </summary>
internal static RemoteSessionCapability CreateServerCapability()
{
return new RemoteSessionCapability(RemotingDestination.Client);
}
/// <summary>
/// This is static property which gets Current TimeZone in byte format
/// by using ByteFormatter.
/// This is static to make client generate this only once.
/// </summary>
internal static byte[] GetCurrentTimeZoneInByteFormat()
{
if (_timeZoneInByteFormat == null)
{
Exception e = null;
try
{
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
#pragma warning disable MSLIB0003
formatter.Serialize(stream, TimeZoneInfo.Local);
#pragma warning restore MSLIB0003
stream.Seek(0, SeekOrigin.Begin);
byte[] result = new byte[stream.Length];
stream.Read(result, 0, (int)stream.Length);
_timeZoneInByteFormat = result;
}
}
catch (ArgumentNullException ane)
{
e = ane;
}
catch (System.Runtime.Serialization.SerializationException sre)
{
e = sre;
}
catch (System.Security.SecurityException se)
{
e = se;
}
// if there is any exception serializing the timezone information
// ignore it and dont try to serialize again.
if (e != null)
{
_timeZoneInByteFormat = Array.Empty<byte>();
}
}
return _timeZoneInByteFormat;
}
/// <summary>
/// Gets the TimeZone of the destination machine. This may be null.
/// </summary>
internal TimeZoneInfo TimeZone
{
get { return _timeZone; }
set { _timeZone = value; }
}
}
/// <summary>
/// The HostDefaultDataId enum.
/// </summary>
internal enum HostDefaultDataId
{
ForegroundColor,
BackgroundColor,
CursorPosition,
WindowPosition,
CursorSize,
BufferSize,
WindowSize,
MaxWindowSize,
MaxPhysicalWindowSize,
WindowTitle,
}
/// <summary>
/// The HostDefaultData class.
/// </summary>
internal class HostDefaultData
{
/// <summary>
/// Data.
/// </summary>
#region DO NOT REMOVE OR RENAME THESE FIELDS - it will break remoting compatibility with Windows PowerShell
private Dictionary<HostDefaultDataId, object> data;
#endregion
/// <summary>
/// Private constructor to force use of Create.
/// </summary>
private HostDefaultData()
{
data = new Dictionary<HostDefaultDataId, object>();
}
/// <summary>
/// Indexer to provide clean access to data.
/// </summary>
internal object this[HostDefaultDataId id]
{
get
{
return this.GetValue(id);
}
}
/// <summary>
/// Has value.
/// </summary>
internal bool HasValue(HostDefaultDataId id)
{
return data.ContainsKey(id);
}
/// <summary>
/// Set value.
/// </summary>
internal void SetValue(HostDefaultDataId id, object dataValue)
{
data[id] = dataValue;
}
/// <summary>
/// Get value.
/// </summary>
internal object GetValue(HostDefaultDataId id)
{
object result;
data.TryGetValue(id, out result);
return result;
}
/// <summary>
/// Returns null if host is null or if reading RawUI fields fails; otherwise returns a valid object.
/// </summary>
internal static HostDefaultData Create(PSHostRawUserInterface hostRawUI)
{
if (hostRawUI == null)
{
return null;
}
HostDefaultData hostDefaultData = new HostDefaultData();
// Try to get values from the host. Catch-all okay because of 3rd party call-out.
// Set ForegroundColor.
try
{
hostDefaultData.SetValue(HostDefaultDataId.ForegroundColor, hostRawUI.ForegroundColor);
}
catch (Exception)
{
}
// Set BackgroundColor.
try
{
hostDefaultData.SetValue(HostDefaultDataId.BackgroundColor, hostRawUI.BackgroundColor);
}
catch (Exception)
{
}
// Set CursorPosition.
try
{
hostDefaultData.SetValue(HostDefaultDataId.CursorPosition, hostRawUI.CursorPosition);
}
catch (Exception)
{
}
// Set WindowPosition.
try
{
hostDefaultData.SetValue(HostDefaultDataId.WindowPosition, hostRawUI.WindowPosition);
}
catch (Exception)
{
}
// Set CursorSize.
try
{
hostDefaultData.SetValue(HostDefaultDataId.CursorSize, hostRawUI.CursorSize);
}
catch (Exception)
{
}
// Set BufferSize.
try
{
hostDefaultData.SetValue(HostDefaultDataId.BufferSize, hostRawUI.BufferSize);
}
catch (Exception)
{
}
// Set WindowSize.
try
{
hostDefaultData.SetValue(HostDefaultDataId.WindowSize, hostRawUI.WindowSize);
}
catch (Exception)
{
}
// Set MaxWindowSize.
try
{
hostDefaultData.SetValue(HostDefaultDataId.MaxWindowSize, hostRawUI.MaxWindowSize);
}
catch (Exception)
{
}
// Set MaxPhysicalWindowSize.
try
{
hostDefaultData.SetValue(HostDefaultDataId.MaxPhysicalWindowSize, hostRawUI.MaxPhysicalWindowSize);
}
catch (Exception)
{
}
// Set WindowTitle.
try
{
hostDefaultData.SetValue(HostDefaultDataId.WindowTitle, hostRawUI.WindowTitle);
}
catch (Exception)
{
}
return hostDefaultData;
}
}
/// <summary>
/// The HostInfo class.
/// </summary>
internal class HostInfo
{
/// <summary>
/// Host default data.
/// </summary>
internal HostDefaultData HostDefaultData
{
get { return _hostDefaultData; }
}
/// <summary>
/// Is host null.
/// </summary>
internal bool IsHostNull
{
get { return _isHostNull; }
}
/// <summary>
/// Is host ui null.
/// </summary>
private bool _isHostUINull;
/// <summary>
/// Is host ui null.
/// </summary>
internal bool IsHostUINull
{
get
{
return _isHostUINull;
}
}
/// <summary>
/// Is host raw ui null.
/// </summary>
private bool _isHostRawUINull;
private readonly bool _isHostNull;
#region DO NOT REMOVE OR RENAME THESE FIELDS - it will break remoting compatibility with Windows PowerShell
private readonly HostDefaultData _hostDefaultData;
private bool _useRunspaceHost;
#endregion
/// <summary>
/// Is host raw ui null.
/// </summary>
internal bool IsHostRawUINull
{
get
{
return _isHostRawUINull;
}
}
/// <summary>
/// Use runspace host.
/// </summary>
internal bool UseRunspaceHost
{
get { return _useRunspaceHost; }
set { _useRunspaceHost = value; }
}
/// <summary>
/// Constructor for HostInfo.
/// </summary>
internal HostInfo(PSHost host)
{
// Set these flags based on investigating the host.
CheckHostChain(host, ref _isHostNull, ref _isHostUINull, ref _isHostRawUINull);
// If raw UI is non-null then get the host-info object.
if (!_isHostUINull && !_isHostRawUINull)
{
_hostDefaultData = HostDefaultData.Create(host.UI.RawUI);
}
}
/// <summary>
/// Check host chain.
/// </summary>
private static void CheckHostChain(PSHost host, ref bool isHostNull, ref bool isHostUINull, ref bool isHostRawUINull)
{
// Set the defaults.
isHostNull = true;
isHostUINull = true;
isHostRawUINull = true;
// Unwrap the host: remove outer InternalHost object.
if (host == null)
{
// If host is null then the bools are correct. Nothing further to do here.
return;
}
else if (host is InternalHost)
{
// This nesting can only be one level deep.
host = ((InternalHost)host).ExternalHost;
}
// At this point we know for sure that the host is not null.
isHostNull = false;
// Verify that the UI is not null.
if (host.UI == null) { return; }
isHostUINull = false;
// Verify that the raw UI is not null.
if (host.UI.RawUI == null) { return; }
isHostRawUINull = false;
}
}
}