Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
MinishellParameterBinderController.cs385 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.ObjectModel;6using System.Globalization;7using System.IO;8using System.Management.Automation.Language;9using System.Xml;10 11using Dbg = System.Management.Automation.Diagnostics;12 13namespace System.Management.Automation14{15    /// <summary>16    /// This is the interface between the NativeCommandProcessor and the17    /// parameter binders required to bind parameters to a minishell.18    /// </summary>19    internal class MinishellParameterBinderController : NativeCommandParameterBinderController20    {21        #region ctor22 23        /// <summary>24        /// Initializes the parameter binder controller for25        /// the specified native command and engine context.26        /// </summary>27        /// <param name="command">28        /// The command that the parameters will be bound to.29        /// </param>30        internal MinishellParameterBinderController(31            NativeCommand command)32            : base(command)33        {34            InputFormat = NativeCommandIOFormat.Xml;35            OutputFormat = NativeCommandIOFormat.Text;36        }37 38        #endregion ctor39 40        /// <summary>41        /// Value of input format. This property should be read after binding of parameters.42        /// </summary>43        internal NativeCommandIOFormat InputFormat { get; private set; }44 45        /// <summary>46        /// Value of output format. This property should be read after binding of parameters.47        /// </summary>48        internal NativeCommandIOFormat OutputFormat { get; private set; }49 50        /// <summary>51        /// IF true, child minishell is invoked with no-window.52        /// </summary>53        internal bool NonInteractive { get; private set; }54 55        /// <summary>56        /// Binds the specified parameters to the native command.57        /// </summary>58        /// <param name="parameters">59        /// The parameters to bind.60        /// </param>61        /// <param name="outputRedirected">62        /// true if minishell output is redirected.63        /// </param>64        /// <param name="hostName">65        /// name of the calling host.66        /// </param>67        /// <remarks>68        /// For any parameters that do not have a name, they are added to the command69        /// line arguments for the command70        /// </remarks>71        internal Collection<CommandParameterInternal> BindParameters(Collection<CommandParameterInternal> parameters, bool outputRedirected, string hostName)72        {73            MinishellParameters seen = 0;74            string inputFormat = null;75            string outputFormat = null;76            for (int i = 0; i < parameters.Count; i++)77            {78                var parameter = parameters[i];79 80                if (parameter.ParameterNameSpecified)81                {82                    var parameterName = parameter.ParameterName;83 84                    if (CommandParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))85                    {86                        HandleSeenParameter(ref seen, MinishellParameters.Command, CommandParameter);87 88                        // Value must be specified for -Command parameter.89                        if (i + 1 >= parameters.Count)90                        {91                            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, CommandParameter,92                                                               typeof(ScriptBlock), null,93                                                               NativeCP.NoValueForCommandParameter,94                                                               "NoValueForCommandParameter");95                        }96 97                        i += 1;98 99                        // Value of -Command parameter must be scriptblock100                        var scriptBlockArgument = parameters[i];101                        var argumentValue = PSObject.Base(scriptBlockArgument.ArgumentValue);102                        if (!scriptBlockArgument.ArgumentSpecified || argumentValue is not ScriptBlock)103                        {104                            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, CommandParameter,105                                                               typeof(ScriptBlock), argumentValue.GetType(),106                                                               NativeCP.IncorrectValueForCommandParameter,107                                                               "IncorrectValueForCommandParameter");108                        }109 110                        // Replace the parameters with -EncodedCommand <base64 encoded scriptblock>111                        parameters[i - 1] = CommandParameterInternal.CreateParameter(EncodedCommandParameter, "-" + EncodedCommandParameter, parameter.ParameterAst);112                        string encodedScript = StringToBase64Converter.StringToBase64String(argumentValue.ToString());113                        parameters[i] = CommandParameterInternal.CreateArgument(encodedScript, scriptBlockArgument.ArgumentAst);114                    }115                    else if (InputFormatParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))116                    {117                        HandleSeenParameter(ref seen, MinishellParameters.InputFormat, InputFormatParameter);118 119                        // Value for -Inputformat must be specified120                        if (i + 1 >= parameters.Count)121                        {122                            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, InputFormatParameter,123                                                               typeof(string), null,124                                                               NativeCP.NoValueForInputFormatParameter,125                                                               "NoValueForInputFormatParameter");126                        }127 128                        // Update the argument (partial arguments are allowed)129                        i += 1;130                        var inputFormatArg = parameters[i];131                        inputFormat = ProcessFormatParameterValue(InputFormatParameter, inputFormatArg.ArgumentValue);132                        parameters[i - 1] = CommandParameterInternal.CreateParameter(InputFormatParameter, "-" + InputFormatParameter, parameter.ParameterAst);133                        parameters[i] = CommandParameterInternal.CreateArgument(inputFormat, inputFormatArg.ArgumentAst);134                    }135                    else if (OutputFormatParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))136                    {137                        HandleSeenParameter(ref seen, MinishellParameters.OutputFormat, OutputFormatParameter);138 139                        // Value for -Inputformat must be specified140                        if (i + 1 >= parameters.Count)141                        {142                            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, OutputFormatParameter,143                                                               typeof(string), null,144                                                               NativeCP.NoValueForOutputFormatParameter,145                                                               "NoValueForInputFormatParameter");146                        }147 148                        // Update the argument (partial arguments are allowed)149                        i += 1;150                        var outputFormatArg = parameters[i];151                        outputFormat = ProcessFormatParameterValue(OutputFormatParameter, outputFormatArg.ArgumentValue);152                        parameters[i - 1] = CommandParameterInternal.CreateParameter(OutputFormatParameter, "-" + OutputFormatParameter, parameter.ParameterAst);153                        parameters[i] = CommandParameterInternal.CreateArgument(outputFormat, outputFormatArg.ArgumentAst);154                    }155                    else if (ArgsParameter.StartsWith(parameterName, StringComparison.OrdinalIgnoreCase))156                    {157                        HandleSeenParameter(ref seen, MinishellParameters.Arguments, ArgsParameter);158 159                        // Value for -Args parameter must be specified160                        if (i + 1 >= parameters.Count)161                        {162                            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, ArgsParameter,163                                                               typeof(string), null, NativeCP.NoValuesSpecifiedForArgs,164                                                               "NoValuesSpecifiedForArgs");165                        }166 167                        // Get the encoded value for -args parameter168                        i += 1;169                        var argsArg = parameters[i];170                        var encodedArgs = ConvertArgsValueToEncodedString(argsArg.ArgumentValue);171                        parameters[i - 1] = CommandParameterInternal.CreateParameter(EncodedArgsParameter, "-" + EncodedArgsParameter, parameter.ParameterAst);172                        // NOTE: do not pass the ArgumentAst; it will fail validation in BindParameters if there173                        // are multiple arguments (array) but encodedArgs is an encoded string.174                        parameters[i] = CommandParameterInternal.CreateArgument(encodedArgs);175                    }176                }177                else178                {179                    // -Command is positional parameter. Bind first scriptblock to it, others are errors.180                    var scriptBlockArgument = parameters[i];181                    var argumentValue = PSObject.Base(scriptBlockArgument.ArgumentValue);182                    if (argumentValue is ScriptBlock)183                    {184                        HandleSeenParameter(ref seen, MinishellParameters.Command, CommandParameter);185 186                        // Replace the argument with -EncodedCommand <base64 encoded scriptblock>187                        string encodedScript = StringToBase64Converter.StringToBase64String(argumentValue.ToString());188                        parameters[i] = CommandParameterInternal.CreateParameterWithArgument(189                            parameter.ArgumentAst, EncodedCommandParameter, "-" + EncodedCommandParameter,190                            parameter.ArgumentAst, encodedScript,191                            spaceAfterParameter: true);192                    }193                }194            }195 196            // Add InputFormat and OutputFormat parameter if not specified197            if (inputFormat == null)198            {199                // For minishell default input format is xml200                parameters.Add(CommandParameterInternal.CreateParameter(InputFormatParameter, "-" + InputFormatParameter));201                parameters.Add(CommandParameterInternal.CreateArgument(XmlFormatValue));202                inputFormat = XmlFormatValue;203            }204 205            if (outputFormat == null)206            {207                // If output is redirected, output format should be xml208                outputFormat = outputRedirected ? XmlFormatValue : TextFormatValue;209                parameters.Add(CommandParameterInternal.CreateParameter(OutputFormatParameter, "-" + OutputFormatParameter));210                parameters.Add(CommandParameterInternal.CreateArgument(outputFormat));211            }212 213            // Set the output and input format class variable214            InputFormat = XmlFormatValue.StartsWith(inputFormat, StringComparison.OrdinalIgnoreCase)215                ? NativeCommandIOFormat.Xml216                : NativeCommandIOFormat.Text;217            OutputFormat = XmlFormatValue.StartsWith(outputFormat, StringComparison.OrdinalIgnoreCase)218                ? NativeCommandIOFormat.Xml219                : NativeCommandIOFormat.Text;220 221            // Note if a minishell is invoked from a non-console host, we need to222            // pass -nonInteractive flag. Our console host's name is "ConsoleHost".223            // Correct check would be see if current host has access to console and224            // pass noninteractive flag if doesn't.225            if (string.IsNullOrEmpty(hostName) || !hostName.Equals("ConsoleHost", StringComparison.OrdinalIgnoreCase))226            {227                NonInteractive = true;228                parameters.Insert(0, CommandParameterInternal.CreateParameter(NonInteractiveParameter, "-" + NonInteractiveParameter));229            }230 231            ((NativeCommandParameterBinder)DefaultParameterBinder).BindParameters(parameters);232 233            Diagnostics.Assert(s_emptyReturnCollection.Count == 0, "This list shouldn't be used for anything as it's shared.");234 235            return s_emptyReturnCollection;236        }237 238        private static readonly Collection<CommandParameterInternal> s_emptyReturnCollection = new Collection<CommandParameterInternal>();239 240        internal const string CommandParameter = "command";241        internal const string EncodedCommandParameter = "encodedCommand";242        internal const string ArgsParameter = "args";243        internal const string EncodedArgsParameter = "encodedarguments";244        internal const string InputFormatParameter = "inputFormat";245        internal const string OutputFormatParameter = "outputFormat";246        internal const string XmlFormatValue = "xml";247        internal const string TextFormatValue = "text";248        internal const string NonInteractiveParameter = "noninteractive";249 250        [Flags]251        private enum MinishellParameters252        {253            Command = 0x01,254            Arguments = 0x02,255            InputFormat = 0x04,256            OutputFormat = 0x08257        }258 259        /// <summary>260        /// Handles error handling if some parameter is specified more than once.261        /// </summary>262        private void HandleSeenParameter(ref MinishellParameters seen, MinishellParameters parameter, string parameterName)263        {264            if ((seen & parameter) == parameter)265            {266                throw NewParameterBindingException(null, ErrorCategory.InvalidArgument,267                                                   "-" + parameterName, null, null,268                                                   NativeCP.ParameterSpecifiedAlready,269                                                   "ParameterSpecifiedAlready",270                                                   parameterName);271            }272            else273            {274                seen |= parameter;275            }276        }277 278        /// <summary>279        /// This function processes the value for -inputFormat and -outputFormat parameter of minishell.280        /// </summary>281        /// <param name="parameterName">Name of the parameter for error messages. Value should be -inputFormat or -outputFormat.</param>282        /// <param name="value">Value to process.</param>283        /// <returns>Processed value.</returns>284        private string285        ProcessFormatParameterValue(string parameterName, object value)286        {287            string fpValue;288            try289            {290                fpValue = (string)LanguagePrimitives.ConvertTo(value, typeof(string), CultureInfo.InvariantCulture);291            }292            catch (PSInvalidCastException ex)293            {294                throw NewParameterBindingException(ex, ErrorCategory.InvalidArgument, parameterName,295                                                   typeof(string), value.GetType(),296                                                   NativeCP.StringValueExpectedForFormatParameter,297                                                   "StringValueExpectedForFormatParameter", parameterName);298            }299 300            if (XmlFormatValue.StartsWith(fpValue, StringComparison.OrdinalIgnoreCase))301            {302                return XmlFormatValue;303            }304 305            if (TextFormatValue.StartsWith(fpValue, StringComparison.OrdinalIgnoreCase))306            {307                return TextFormatValue;308            }309 310            throw NewParameterBindingException(null, ErrorCategory.InvalidArgument, parameterName,311                typeof(string), value.GetType(),312                NativeCP.IncorrectValueForFormatParameter,313                "IncorrectValueForFormatParameter",314                fpValue, parameterName);315        }316 317        /// <summary>318        /// Converts value of args parameter in to an encoded string.319        /// </summary>320        private static string ConvertArgsValueToEncodedString(object value)321        {322            ArrayList list = ConvertArgsValueToArrayList(value);323 324            // Serialize the list325            StringWriter stringWriter = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);326            // When (if) switching to XmlTextWriter.Create remember the OmitXmlDeclaration difference327            XmlWriter xmlWriter = XmlWriter.Create(stringWriter);328            Serializer serializer = new Serializer(xmlWriter);329            serializer.Serialize(list);330            serializer.Done();331            xmlWriter.Flush();332            string result = stringWriter.ToString();333 334            // convert result to encoded string335            return StringToBase64Converter.StringToBase64String(result);336        }337 338        /// <summary>339        /// Converts the value of -args parameter received from340        /// parser in to an arraylist.341        /// </summary>342        private static ArrayList ConvertArgsValueToArrayList(object value)343        {344            ArrayList results = new ArrayList();345            IEnumerator list = LanguagePrimitives.GetEnumerator(value);346            if (list == null)347            {348                results.Add(value);349            }350            else351            {352                while (list.MoveNext())353                {354                    results.Add(list.Current);355                }356            }357 358            return results;359        }360 361        private ParameterBindingException NewParameterBindingException(362            Exception innerException,363            ErrorCategory errorCategory,364            string parameterName,365            Type parameterType,366            Type typeSpecified,367            string resourceString,368            string errorId,369            params object[] args)370        {371            return new ParameterBindingException(372                innerException,373                errorCategory,374                this.InvocationInfo,375                null,376                parameterName,377                parameterType,378                typeSpecified,379                resourceString,380                errorId,381                args);382        }383    }384}385