462 lines
13 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|