mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
- Remove the year about copyright - Fix casing of `All rights reserved` - Replace Unicode characters representing single quote with a single quote
409 lines
19 KiB
C#
409 lines
19 KiB
C#
/********************************************************************++
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
--********************************************************************/
|
|
|
|
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Management.Automation.Language;
|
|
using System.Xml;
|
|
using System.Globalization;
|
|
using Dbg = System.Management.Automation.Diagnostics;
|
|
|
|
namespace System.Management.Automation
|
|
{
|
|
/// <summary>
|
|
/// This is the interface between the NativeCommandProcessor and the
|
|
/// parameter binders required to bind parameters to a minishell.
|
|
/// </summary>
|
|
///
|
|
internal class MinishellParameterBinderController : NativeCommandParameterBinderController
|
|
{
|
|
#region ctor
|
|
|
|
/// <summary>
|
|
/// Initializes the parameter binder controller for
|
|
/// the specified native command and engine context
|
|
/// </summary>
|
|
///
|
|
/// <param name="command">
|
|
/// The command that the parameters will be bound to.
|
|
/// </param>
|
|
///
|
|
internal MinishellParameterBinderController(
|
|
NativeCommand command)
|
|
: base(command)
|
|
{
|
|
InputFormat = NativeCommandIOFormat.Xml;
|
|
OutputFormat = NativeCommandIOFormat.Text;
|
|
}
|
|
|
|
#endregion ctor
|
|
|
|
/// <summary>
|
|
/// Override of parent class which should not be used.
|
|
/// </summary>
|
|
///
|
|
/// <param name="parameters">
|
|
/// The parameters to bind.
|
|
/// </param>
|
|
///
|
|
/// <remarks>
|
|
/// For any parameters that do not have a name, they are added to the command
|
|
/// line arguments for the command
|
|
/// </remarks>
|
|
internal override
|
|
Collection<CommandParameterInternal>
|
|
BindParameters(Collection<CommandParameterInternal> parameters)
|
|
{
|
|
Dbg.Assert(false, "this method should be used");
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Value of input format. This property should be read after binding of parameters.
|
|
/// </summary>
|
|
internal NativeCommandIOFormat InputFormat { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Value of output format. This property should be read after binding of parameters.
|
|
/// </summary>
|
|
internal NativeCommandIOFormat OutputFormat { get; private set; }
|
|
|
|
/// <summary>
|
|
/// IF true, child minishell is invoked with no-window
|
|
/// </summary>
|
|
internal bool NonInteractive { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Binds the specified parameters to the native command
|
|
/// </summary>
|
|
///
|
|
/// <param name="parameters">
|
|
/// The parameters to bind.
|
|
/// </param>
|
|
///
|
|
/// <param name="outputRedirected">
|
|
/// true if minishell output is redirected.
|
|
/// </param>
|
|
///
|
|
/// <param name="hostName">
|
|
/// name of the calling host.
|
|
/// </param>
|
|
///
|
|
/// <remarks>
|
|
/// For any parameters that do not have a name, they are added to the command
|
|
/// line arguments for the command
|
|
/// </remarks>
|
|
///
|
|
internal Collection<CommandParameterInternal> BindParameters(Collection<CommandParameterInternal> parameters, bool outputRedirected, string hostName)
|
|
{
|
|
MinishellParameters seen = 0;
|
|
string inputFormat = null;
|
|
string outputFormat = null;
|
|
for (int i = 0; i < parameters.Count; i++)
|
|
{
|
|
var parameter = parameters[i];
|
|
|
|
if (parameter.ParameterNameSpecified)
|
|
{
|
|
var parameterName = parameter.ParameterName;
|
|
|
|
if (CommandParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
HandleSeenParameter(ref seen, MinishellParameters.Command, CommandParameter);
|
|
|
|
// Value must be specified for -Command parameter.
|
|
if (i + 1 >= parameters.Count)
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, CommandParameter,
|
|
typeof(ScriptBlock), null,
|
|
NativeCP.NoValueForCommandParameter,
|
|
"NoValueForCommandParameter");
|
|
}
|
|
|
|
i += 1;
|
|
|
|
// Value of -Command parameter must be scriptblock
|
|
var scriptBlockArgument = parameters[i];
|
|
var argumentValue = PSObject.Base(scriptBlockArgument.ArgumentValue);
|
|
if (!scriptBlockArgument.ArgumentSpecified || !(argumentValue is ScriptBlock))
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, CommandParameter,
|
|
typeof(ScriptBlock), argumentValue.GetType(),
|
|
NativeCP.IncorrectValueForCommandParameter,
|
|
"IncorrectValueForCommandParameter");
|
|
}
|
|
|
|
// Replace the parameters with -EncodedCommand <base64 encoded scriptblock>
|
|
parameters[i - 1] = CommandParameterInternal.CreateParameter(parameter.ParameterExtent, EncodedCommandParameter, "-" + EncodedCommandParameter);
|
|
string encodedScript = StringToBase64Converter.StringToBase64String(argumentValue.ToString());
|
|
parameters[i] = CommandParameterInternal.CreateArgument(scriptBlockArgument.ArgumentExtent, encodedScript);
|
|
}
|
|
else if (InputFormatParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
HandleSeenParameter(ref seen, MinishellParameters.InputFormat, InputFormatParameter);
|
|
|
|
// Value for -Inputformat must be specified
|
|
if (i + 1 >= parameters.Count)
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, InputFormatParameter,
|
|
typeof(string), null,
|
|
NativeCP.NoValueForInputFormatParameter,
|
|
"NoValueForInputFormatParameter");
|
|
}
|
|
|
|
// Update the argument (partial arguments are allowed)
|
|
i += 1;
|
|
var inputFormatArg = parameters[i];
|
|
inputFormat = ProcessFormatParameterValue(InputFormatParameter, inputFormatArg.ArgumentValue);
|
|
parameters[i - 1] = CommandParameterInternal.CreateParameter(parameter.ParameterExtent, InputFormatParameter, "-" + InputFormatParameter);
|
|
parameters[i] = CommandParameterInternal.CreateArgument(inputFormatArg.ArgumentExtent, inputFormat);
|
|
}
|
|
else if (OutputFormatParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
HandleSeenParameter(ref seen, MinishellParameters.OutputFormat, OutputFormatParameter);
|
|
|
|
// Value for -Inputformat must be specified
|
|
if (i + 1 >= parameters.Count)
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, OutputFormatParameter,
|
|
typeof(string), null,
|
|
NativeCP.NoValueForOutputFormatParameter,
|
|
"NoValueForInputFormatParameter");
|
|
}
|
|
|
|
// Update the argument (partial arguments are allowed)
|
|
i += 1;
|
|
var outputFormatArg = parameters[i];
|
|
outputFormat = ProcessFormatParameterValue(OutputFormatParameter, outputFormatArg.ArgumentValue);
|
|
parameters[i - 1] = CommandParameterInternal.CreateParameter(parameter.ParameterExtent, OutputFormatParameter, "-" + OutputFormatParameter);
|
|
parameters[i] = CommandParameterInternal.CreateArgument(outputFormatArg.ArgumentExtent, outputFormat);
|
|
}
|
|
else if (ArgsParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
HandleSeenParameter(ref seen, MinishellParameters.Arguments, ArgsParameter);
|
|
|
|
// Value for -Args parameter must be specified
|
|
if (i + 1 >= parameters.Count)
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, ArgsParameter,
|
|
typeof(string), null, NativeCP.NoValuesSpecifiedForArgs,
|
|
"NoValuesSpecifiedForArgs");
|
|
}
|
|
|
|
// Get the encoded value for -args parameter
|
|
i += 1;
|
|
var argsArg = parameters[i];
|
|
var encodedArgs = ConvertArgsValueToEncodedString(argsArg.ArgumentValue);
|
|
parameters[i - 1] = CommandParameterInternal.CreateParameter(parameter.ParameterExtent, EncodedArgsParameter, "-" + EncodedArgsParameter);
|
|
parameters[i] = CommandParameterInternal.CreateArgument(argsArg.ArgumentExtent, encodedArgs);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// -Command is positional parameter. Bind first scriptblock to it, others are errors.
|
|
var scriptBlockArgument = parameters[i];
|
|
var argumentValue = PSObject.Base(scriptBlockArgument.ArgumentValue);
|
|
if (argumentValue is ScriptBlock)
|
|
{
|
|
HandleSeenParameter(ref seen, MinishellParameters.Command, CommandParameter);
|
|
|
|
// Replace the argument with -EncodedCommand <base64 encoded scriptblock>
|
|
string encodedScript = StringToBase64Converter.StringToBase64String(argumentValue.ToString());
|
|
parameters[i] = CommandParameterInternal.CreateParameterWithArgument(
|
|
parameter.ArgumentExtent, EncodedCommandParameter, "-" + EncodedCommandParameter,
|
|
parameter.ArgumentExtent, encodedScript,
|
|
spaceAfterParameter: true, arrayIsSingleArgumentForNativeCommand: false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add InputFormat and OutputFormat parameter if not specified
|
|
if (inputFormat == null)
|
|
{
|
|
// For minishell default input format is xml
|
|
parameters.Add(CommandParameterInternal.CreateParameter(PositionUtilities.EmptyExtent, InputFormatParameter, "-" + InputFormatParameter));
|
|
parameters.Add(CommandParameterInternal.CreateArgument(PositionUtilities.EmptyExtent, XmlFormatValue));
|
|
inputFormat = XmlFormatValue;
|
|
}
|
|
|
|
if (outputFormat == null)
|
|
{
|
|
// If output is redirected, output format should be xml
|
|
outputFormat = outputRedirected ? XmlFormatValue : TextFormatValue;
|
|
parameters.Add(CommandParameterInternal.CreateParameter(PositionUtilities.EmptyExtent, OutputFormatParameter, "-" + OutputFormatParameter));
|
|
parameters.Add(CommandParameterInternal.CreateArgument(PositionUtilities.EmptyExtent, outputFormat));
|
|
}
|
|
|
|
// Set the output and input format class variable
|
|
InputFormat = XmlFormatValue.StartsWith(inputFormat, StringComparison.OrdinalIgnoreCase)
|
|
? NativeCommandIOFormat.Xml
|
|
: NativeCommandIOFormat.Text;
|
|
OutputFormat = XmlFormatValue.StartsWith(outputFormat, StringComparison.OrdinalIgnoreCase)
|
|
? NativeCommandIOFormat.Xml
|
|
: NativeCommandIOFormat.Text;
|
|
|
|
// Note if a minishell is invoked from a non-console host, we need to
|
|
// pass -nonInteractive flag. Our console host's name is "ConsoleHost".
|
|
// Correct check would be see if current host has access to console and
|
|
// pass noninteractive flag if doesn't.
|
|
if (string.IsNullOrEmpty(hostName) || !hostName.Equals("ConsoleHost", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
NonInteractive = true;
|
|
parameters.Insert(0, CommandParameterInternal.CreateParameter(PositionUtilities.EmptyExtent, NonInteractiveParameter, "-" + NonInteractiveParameter));
|
|
}
|
|
|
|
((NativeCommandParameterBinder)DefaultParameterBinder).BindParameters(parameters);
|
|
|
|
Diagnostics.Assert(s_emptyReturnCollection.Count == 0, "This list shouldn't be used for anything as it's shared.");
|
|
|
|
return s_emptyReturnCollection;
|
|
} // BindParameters
|
|
|
|
private static readonly Collection<CommandParameterInternal> s_emptyReturnCollection = new Collection<CommandParameterInternal>();
|
|
|
|
internal const string CommandParameter = "command";
|
|
internal const string EncodedCommandParameter = "encodedCommand";
|
|
internal const string ArgsParameter = "args";
|
|
internal const string EncodedArgsParameter = "encodedarguments";
|
|
internal const string InputFormatParameter = "inputFormat";
|
|
internal const string OutputFormatParameter = "outputFormat";
|
|
internal const string XmlFormatValue = "xml";
|
|
internal const string TextFormatValue = "text";
|
|
internal const string NonInteractiveParameter = "noninteractive";
|
|
|
|
[Flags]
|
|
private enum MinishellParameters
|
|
{
|
|
Command = 0x01,
|
|
Arguments = 0x02,
|
|
InputFormat = 0x04,
|
|
OutputFormat = 0x08
|
|
};
|
|
|
|
/// <summary>
|
|
/// Handles error handling if some parameter is specified more than once.
|
|
/// </summary>
|
|
private void HandleSeenParameter(ref MinishellParameters seen, MinishellParameters parameter, string parameterName)
|
|
{
|
|
if ((seen & parameter) == parameter)
|
|
{
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument,
|
|
"-" + parameterName, null, null,
|
|
NativeCP.ParameterSpecifiedAlready,
|
|
"ParameterSpecifiedAlready",
|
|
parameterName);
|
|
}
|
|
else
|
|
{
|
|
seen = seen | parameter;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function processes the value for -inputFormat and -outputFormat parameter of minishell.
|
|
/// </summary>
|
|
/// <param name="parameterName">Name of the parameter for error messages. Value should be -inputFormat or -outputFormat</param>
|
|
/// <param name="value">value to process</param>
|
|
/// <returns>Processed value</returns>
|
|
private string
|
|
ProcessFormatParameterValue(string parameterName, object value)
|
|
{
|
|
string fpValue;
|
|
try
|
|
{
|
|
fpValue = (string)LanguagePrimitives.ConvertTo(value, typeof(string), CultureInfo.InvariantCulture);
|
|
}
|
|
catch (PSInvalidCastException ex)
|
|
{
|
|
throw NewParameterBindingException(ex, ErrorCategory.InvalidArgument, parameterName,
|
|
typeof(string), value.GetType(),
|
|
NativeCP.StringValueExpectedForFormatParameter,
|
|
"StringValueExpectedForFormatParameter", parameterName);
|
|
}
|
|
|
|
if (XmlFormatValue.StartsWith(fpValue, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return XmlFormatValue;
|
|
}
|
|
if (TextFormatValue.StartsWith(fpValue, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return TextFormatValue;
|
|
}
|
|
|
|
throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, parameterName,
|
|
typeof(string), value.GetType(),
|
|
NativeCP.IncorrectValueForFormatParameter,
|
|
"IncorrectValueForFormatParameter",
|
|
fpValue, parameterName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts value of args parameter in to an encoded string
|
|
/// </summary>
|
|
private static string ConvertArgsValueToEncodedString(object value)
|
|
{
|
|
ArrayList list = ConvertArgsValueToArrayList(value);
|
|
|
|
//Serialize the list
|
|
StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
|
|
//When (if) switching to XmlTextWriter.Create remember the OmitXmlDeclaration difference
|
|
XmlWriter xmlWriter = XmlWriter.Create(stringWriter);
|
|
Serializer serializer = new Serializer(xmlWriter);
|
|
serializer.Serialize(list);
|
|
serializer.Done();
|
|
xmlWriter.Flush();
|
|
string result = stringWriter.ToString();
|
|
|
|
//convert result to encoded string
|
|
return StringToBase64Converter.StringToBase64String(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the value of -args parameter received from
|
|
/// parser in to an arraylist
|
|
/// </summary>
|
|
private static ArrayList ConvertArgsValueToArrayList(object value)
|
|
{
|
|
ArrayList results = new ArrayList();
|
|
IEnumerator list = LanguagePrimitives.GetEnumerator(value);
|
|
if (list == null)
|
|
{
|
|
results.Add(value);
|
|
}
|
|
else
|
|
{
|
|
while (list.MoveNext())
|
|
{
|
|
results.Add(list.Current);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private ParameterBindingException NewParameterBindingException(
|
|
Exception innerException,
|
|
ErrorCategory errorCategory,
|
|
string parameterName,
|
|
Type parameterType,
|
|
Type typeSpecified,
|
|
string resourceString,
|
|
string errorId,
|
|
params object[] args)
|
|
{
|
|
return new ParameterBindingException(
|
|
innerException,
|
|
errorCategory,
|
|
this.InvocationInfo,
|
|
null,
|
|
parameterName,
|
|
parameterType,
|
|
typeSpecified,
|
|
resourceString,
|
|
errorId,
|
|
args);
|
|
}
|
|
}
|
|
}
|