// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #region Using directives using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Management.Automation; using System.Net; using System.Text; using Microsoft.Management.Infrastructure.Options; #endregion namespace Microsoft.Management.Infrastructure.CimCmdlets { #region Parameter Set Resolving Classes /// /// /// Define class ParameterDefinitionEntry. /// /// internal class ParameterDefinitionEntry { /// /// Constructor. /// /// /// internal ParameterDefinitionEntry(string parameterSetName, bool mandatory) { this.mandatory = mandatory; this.parameterSetName = parameterSetName; } /// /// Property ParameterSetName. /// internal string ParameterSetName { get { return this.parameterSetName; } } private readonly string parameterSetName = null; /// /// Whether the parameter is mandatory to the set. /// internal bool IsMandatory { get { return this.mandatory; } } private readonly bool mandatory = false; } /// /// /// Define class ParameterSetEntry. /// /// internal class ParameterSetEntry { /// /// Constructor. /// /// internal ParameterSetEntry(UInt32 mandatoryParameterCount) { this.mandatoryParameterCount = mandatoryParameterCount; this.isDefaultParameterSet = false; reset(); } /// /// Constructor. /// /// internal ParameterSetEntry(ParameterSetEntry toClone) { this.mandatoryParameterCount = toClone.MandatoryParameterCount; this.isDefaultParameterSet = toClone.IsDefaultParameterSet; reset(); } /// /// Constructor. /// /// /// internal ParameterSetEntry(UInt32 mandatoryParameterCount, bool isDefault) { this.mandatoryParameterCount = mandatoryParameterCount; this.isDefaultParameterSet = isDefault; reset(); } /// /// Reset the internal status. /// internal void reset() { this.setMandatoryParameterCount = this.setMandatoryParameterCountAtBeginProcess; this.isValueSet = this.isValueSetAtBeginProcess; } /// /// Property DefaultParameterSet /// internal bool IsDefaultParameterSet { get { return this.isDefaultParameterSet; } } private readonly bool isDefaultParameterSet = false; /// /// Property MandatoryParameterCount /// internal UInt32 MandatoryParameterCount { get { return this.mandatoryParameterCount; } } private readonly UInt32 mandatoryParameterCount = 0; /// /// Property IsValueSet /// internal bool IsValueSet { get { return this.isValueSet; } set { this.isValueSet = value; } } private bool isValueSet = false; /// /// Property IsValueSetAtBeginProcess /// internal bool IsValueSetAtBeginProcess { get { return this.isValueSetAtBeginProcess; } set { this.isValueSetAtBeginProcess = value; } } private bool isValueSetAtBeginProcess = false; /// /// Property SetMandatoryParameterCount /// internal UInt32 SetMandatoryParameterCount { get { return this.setMandatoryParameterCount; } set { this.setMandatoryParameterCount = value; } } private UInt32 setMandatoryParameterCount = 0; /// /// Property SetMandatoryParameterCountAtBeginProcess /// internal UInt32 SetMandatoryParameterCountAtBeginProcess { get { return this.setMandatoryParameterCountAtBeginProcess; } set { this.setMandatoryParameterCountAtBeginProcess = value; } } private UInt32 setMandatoryParameterCountAtBeginProcess = 0; } /// /// Define class ParameterBinder. /// internal class ParameterBinder { /// /// Constructor. /// /// /// internal ParameterBinder( Dictionary> parameters, Dictionary sets) { this.CloneParameterEntries(parameters, sets); } #region Two dictionaries used to determine the bound parameter set /// /// Define the parameter definition entries, /// each parameter may belong a set of parameterSets, each parameter set /// are defined by a . /// private Dictionary> parameterDefinitionEntries; /// /// /// Define parameter set entries, /// each cmdlet has a list of parameter set, each has number of mandatory parameters, etc. /// This data structure is used to track the number of mandatory parameter has been set for /// current parameterset, whether the parameter set was been set by user. /// /// private Dictionary parameterSetEntries; #endregion /// /// /// Used to remember the set of parameterset were set /// if any conflict occurred with current parameter, /// throw exception /// /// private List parametersetNamesList = new List(); /// /// Parameter names list. /// private List parameterNamesList = new List(); /// /// /// Used to remember the set of parameterset were set before begin process /// if any conflict occurred with current parameter, /// throw exception /// /// private List parametersetNamesListAtBeginProcess = new List(); /// /// Parameter names list before begin process. /// private List parameterNamesListAtBeginProcess = new List(); /// /// /// Reset the status of parameter set entries /// /// internal void reset() { foreach (KeyValuePair setEntry in parameterSetEntries) { setEntry.Value.reset(); } this.parametersetNamesList.Clear(); foreach (string parametersetName in this.parametersetNamesListAtBeginProcess) { this.parametersetNamesList.Add(parametersetName); } this.parameterNamesList.Clear(); foreach (string parameterName in this.parameterNamesListAtBeginProcess) { this.parameterNamesList.Add(parameterName); } } /// /// /// A given parameter's value was set by cmdlet caller, /// check and change the status of parameter set, /// throw exception if confliction occurred /// /// /// /// Throw if conflict parameter was set. internal void SetParameter(string parameterName, bool isBeginProcess) { DebugHelper.WriteLogEx("ParameterName = {0}, isBeginProcess = {1}", 0, parameterName, isBeginProcess); if (this.parameterNamesList.Contains(parameterName)) { DebugHelper.WriteLogEx("ParameterName {0} is already bound ", 1, parameterName); return; } else { this.parameterNamesList.Add(parameterName); if (isBeginProcess) { this.parameterNamesListAtBeginProcess.Add(parameterName); } } if (this.parametersetNamesList.Count == 0) { List nameset = new List(); foreach (ParameterDefinitionEntry parameterDefinitionEntry in this.parameterDefinitionEntries[parameterName]) { DebugHelper.WriteLogEx("parameterset name = '{0}'; mandatory = '{1}'", 1, parameterDefinitionEntry.ParameterSetName, parameterDefinitionEntry.IsMandatory); ParameterSetEntry psEntry = this.parameterSetEntries[parameterDefinitionEntry.ParameterSetName]; if (psEntry == null) continue; if (parameterDefinitionEntry.IsMandatory) { psEntry.SetMandatoryParameterCount++; if (isBeginProcess) { psEntry.SetMandatoryParameterCountAtBeginProcess++; } DebugHelper.WriteLogEx("parameterset name = '{0}'; SetMandatoryParameterCount = '{1}'", 1, parameterDefinitionEntry.ParameterSetName, psEntry.SetMandatoryParameterCount); } if (!psEntry.IsValueSet) { psEntry.IsValueSet = true; if (isBeginProcess) { psEntry.IsValueSetAtBeginProcess = true; } } nameset.Add(parameterDefinitionEntry.ParameterSetName); } this.parametersetNamesList = nameset; if (isBeginProcess) { this.parametersetNamesListAtBeginProcess = nameset; } } else { List nameset = new List(); foreach (ParameterDefinitionEntry entry in this.parameterDefinitionEntries[parameterName]) { if (this.parametersetNamesList.Contains(entry.ParameterSetName)) { nameset.Add(entry.ParameterSetName); if (entry.IsMandatory) { ParameterSetEntry psEntry = this.parameterSetEntries[entry.ParameterSetName]; psEntry.SetMandatoryParameterCount++; if (isBeginProcess) { psEntry.SetMandatoryParameterCountAtBeginProcess++; } DebugHelper.WriteLogEx("parameterset name = '{0}'; SetMandatoryParameterCount = '{1}'", 1, entry.ParameterSetName, psEntry.SetMandatoryParameterCount); } } } if (nameset.Count == 0) { throw new PSArgumentException(Strings.UnableToResolveParameterSetName); } else { this.parametersetNamesList = nameset; if (isBeginProcess) { this.parametersetNamesListAtBeginProcess = nameset; } } } } /// /// Get the parameter set name based on current binding results. /// /// internal string GetParameterSet() { DebugHelper.WriteLogEx(); string boundParameterSetName = null; string defaultParameterSetName = null; List noMandatoryParameterSet = new List(); // Looking for parameter set which have mandatory parameters foreach (string parameterSetName in this.parameterSetEntries.Keys) { ParameterSetEntry entry = this.parameterSetEntries[parameterSetName]; DebugHelper.WriteLogEx( "parameterset name = {0}, {1}/{2} mandatory parameters.", 1, parameterSetName, entry.SetMandatoryParameterCount, entry.MandatoryParameterCount); // Ignore the parameter set which has no mandatory parameter firstly if (entry.MandatoryParameterCount == 0) { if (entry.IsDefaultParameterSet) { defaultParameterSetName = parameterSetName; } if (entry.IsValueSet) { noMandatoryParameterSet.Add(parameterSetName); } continue; } if ((entry.SetMandatoryParameterCount == entry.MandatoryParameterCount) && this.parametersetNamesList.Contains(parameterSetName)) { if (boundParameterSetName != null) { throw new PSArgumentException(Strings.UnableToResolveParameterSetName); } boundParameterSetName = parameterSetName; } } // Looking for parameter set which has no mandatory parameters if (boundParameterSetName == null) { // throw if there are > 1 parameter set if (noMandatoryParameterSet.Count > 1) { throw new PSArgumentException(Strings.UnableToResolveParameterSetName); } else if (noMandatoryParameterSet.Count == 1) { boundParameterSetName = noMandatoryParameterSet[0]; } } // Looking for default parameter set if (boundParameterSetName == null) { boundParameterSetName = defaultParameterSetName; } // throw if still can not find the parameter set name if (boundParameterSetName == null) { throw new PSArgumentException(Strings.UnableToResolveParameterSetName); } return boundParameterSetName; } /// /// Deep clone the parameter entries to member variable. /// private void CloneParameterEntries( Dictionary> parameters, Dictionary sets) { this.parameterDefinitionEntries = parameters; this.parameterSetEntries = new Dictionary(); foreach (KeyValuePair parameterSet in sets) { this.parameterSetEntries.Add(parameterSet.Key, new ParameterSetEntry(parameterSet.Value)); } } } #endregion /// /// Base command for all cim cmdlets. /// public class CimBaseCommand : Cmdlet, IDisposable { #region resolve parameter set name /// /// /// Check set parameters and set ParameterSetName /// /// /// Following are special types to be handled /// Microsoft.Management.Infrastructure.Options.PacketEncoding /// Microsoft.Management.Infrastructure.Options.ImpersonationType /// UInt32 /// Authentication.None (default value)? /// ProxyType.None /// /// internal void CheckParameterSet() { if (this.parameterBinder != null) { try { this.parameterSetName = this.parameterBinder.GetParameterSet(); } finally { this.parameterBinder.reset(); } } DebugHelper.WriteLog("current parameterset is: " + this.parameterSetName, 4); } /// /// Redirect to parameterBinder to set one parameter. /// /// internal void SetParameter(object value, string parameterName) { // Ignore the null value being set, // Null value could be set by caller unintentionally, // or by powershell to reset the parameter to default value // before the next parameter binding, and ProcessRecord call if (value == null) { return; } if (this.parameterBinder != null) { this.parameterBinder.SetParameter(parameterName, this.AtBeginProcess); } } #endregion #region constructors /// /// Constructor. /// internal CimBaseCommand() { this.disposed = false; this.parameterBinder = null; } /// /// Constructor. /// internal CimBaseCommand(Dictionary> parameters, Dictionary sets) { this.disposed = false; this.parameterBinder = new ParameterBinder(parameters, sets); } #endregion #region override functions of Cmdlet /// /// StopProcessing method. /// protected override void StopProcessing() { Dispose(); } #endregion #region IDisposable interface /// /// IDisposable interface. /// private bool disposed; /// /// /// Dispose() calls Dispose(true). /// Implement IDisposable. Do not make this method virtual. /// A derived class should not be able to override this method. /// /// public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } /// /// /// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// /// Whether it is directly called. protected void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { DisposeInternal(); } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } } /// /// Clean up resources. /// protected virtual void DisposeInternal() { // Dispose managed resources. if (this.operation != null) { this.operation.Dispose(); } } #endregion #region private members /// /// Parameter binder used to resolve parameter set name. /// private ParameterBinder parameterBinder; /// /// /// Async operation handler /// /// private CimAsyncOperation operation; /// /// Lock object. /// private readonly object myLock = new object(); /// /// /// parameter set name /// /// private string parameterSetName; /// /// This flag is introduced to resolve the parameter set name /// during process record /// Whether at begin process time, false means in processrecord. /// private bool atBeginProcess = true; internal bool AtBeginProcess { get { return this.atBeginProcess; } set { this.atBeginProcess = value; } } #endregion #region internal properties /// /// /// Set object, to which /// current cmdlet will delegate all operations. /// /// internal CimAsyncOperation AsyncOperation { set { lock (this.myLock) { Debug.Assert(this.operation == null, "Caller should verify that operation is null"); this.operation = value; } } get { return this.operation; } } /// /// /// Get current ParameterSetName of the cmdlet /// /// internal string ParameterSetName { get { return this.parameterSetName; } } /// /// Gets/Sets cmdlet operation wrapper object. /// internal virtual CmdletOperationBase CmdletOperation { get; set; } /// /// /// Throw terminating error /// /// internal void ThrowTerminatingError(Exception exception, string operation) { ErrorRecord errorRecord = new ErrorRecord(exception, operation, ErrorCategory.InvalidOperation, this); this.CmdletOperation.ThrowTerminatingError(errorRecord); } #endregion #region internal const strings /// /// Alias CN - computer name. /// internal const string AliasCN = "CN"; /// /// Alias ServerName - computer name. /// internal const string AliasServerName = "ServerName"; /// /// Alias OT - operation timeout. /// internal const string AliasOT = "OT"; /// /// Session set name. /// internal const string SessionSetName = "SessionSet"; /// /// Computer set name. /// internal const string ComputerSetName = "ComputerSet"; /// /// Class name computer set name. /// internal const string ClassNameComputerSet = "ClassNameComputerSet"; /// /// Resource Uri computer set name. /// internal const string ResourceUriComputerSet = "ResourceUriComputerSet"; /// /// computer set name. /// internal const string CimInstanceComputerSet = "CimInstanceComputerSet"; /// /// Query computer set name. /// internal const string QueryComputerSet = "QueryComputerSet"; /// /// Class name session set name. /// internal const string ClassNameSessionSet = "ClassNameSessionSet"; /// /// Resource Uri session set name. /// internal const string ResourceUriSessionSet = "ResourceUriSessionSet"; /// /// session set name. /// internal const string CimInstanceSessionSet = "CimInstanceSessionSet"; /// /// Query session set name. /// internal const string QuerySessionSet = "QuerySessionSet"; /// /// computer set name. /// internal const string CimClassComputerSet = "CimClassComputerSet"; /// /// session set name. /// internal const string CimClassSessionSet = "CimClassSessionSet"; #region Session related parameter set name internal const string ComputerNameSet = "ComputerNameSet"; internal const string SessionIdSet = "SessionIdSet"; internal const string InstanceIdSet = "InstanceIdSet"; internal const string NameSet = "NameSet"; internal const string CimSessionSet = "CimSessionSet"; internal const string WSManParameterSet = "WSManParameterSet"; internal const string DcomParameterSet = "DcomParameterSet"; internal const string ProtocolNameParameterSet = "ProtocolTypeSet"; #endregion #region register cimindication parameter set name internal const string QueryExpressionSessionSet = "QueryExpressionSessionSet"; internal const string QueryExpressionComputerSet = "QueryExpressionComputerSet"; #endregion /// /// Credential parameter set. /// internal const string CredentialParameterSet = "CredentialParameterSet"; /// /// Certificate parameter set. /// internal const string CertificateParameterSet = "CertificateParameterSet"; /// /// CimInstance parameter alias. /// internal const string AliasCimInstance = "CimInstance"; #endregion #region internal helper function /// /// /// Throw invalid AuthenticationType /// /// /// /// /// internal void ThrowInvalidAuthenticationTypeError( string operationName, string parameterName, PasswordAuthenticationMechanism authentication) { string message = string.Format(CultureInfo.CurrentUICulture, Strings.InvalidAuthenticationTypeWithNullCredential, authentication, ImpersonatedAuthenticationMechanism.None, ImpersonatedAuthenticationMechanism.Negotiate, ImpersonatedAuthenticationMechanism.Kerberos, ImpersonatedAuthenticationMechanism.NtlmDomain); PSArgumentOutOfRangeException exception = new PSArgumentOutOfRangeException( parameterName, authentication, message); ThrowTerminatingError(exception, operationName); } /// /// Throw conflict parameter error. /// /// /// /// internal void ThrowConflictParameterWasSet( string operationName, string parameterName, string conflictParameterName) { string message = string.Format(CultureInfo.CurrentUICulture, Strings.ConflictParameterWasSet, parameterName, conflictParameterName); PSArgumentException exception = new PSArgumentException(message, parameterName); ThrowTerminatingError(exception, operationName); } /// /// /// Throw not found property error /// /// internal void ThrowInvalidProperty( IEnumerable propertiesList, string className, string parameterName, string operationName, IDictionary actualValue) { StringBuilder propList = new StringBuilder(); foreach (string property in propertiesList) { if (propList.Length > 0) { propList.Append(","); } propList.Append(property); } string message = string.Format(CultureInfo.CurrentUICulture, Strings.CouldNotFindPropertyFromGivenClass, className, propList); PSArgumentOutOfRangeException exception = new PSArgumentOutOfRangeException( parameterName, actualValue, message); ThrowTerminatingError(exception, operationName); } /// /// Create credentials based on given authentication type and PSCredential. /// /// /// /// internal CimCredential CreateCimCredentials(PSCredential psCredentials, PasswordAuthenticationMechanism passwordAuthentication, string operationName, string parameterName) { DebugHelper.WriteLogEx("PSCredential:{0}; PasswordAuthenticationMechanism:{1}; operationName:{2}; parameterName:{3}.", 0, psCredentials, passwordAuthentication, operationName, parameterName); CimCredential credentials = null; if (psCredentials != null) { NetworkCredential networkCredential = psCredentials.GetNetworkCredential(); DebugHelper.WriteLog("Domain:{0}; UserName:{1}; Password:{2}.", 1, networkCredential.Domain, networkCredential.UserName, psCredentials.Password); credentials = new CimCredential(passwordAuthentication, networkCredential.Domain, networkCredential.UserName, psCredentials.Password); } else { ImpersonatedAuthenticationMechanism impersonatedAuthentication; switch (passwordAuthentication) { case PasswordAuthenticationMechanism.Default: impersonatedAuthentication = ImpersonatedAuthenticationMechanism.None; break; case PasswordAuthenticationMechanism.Negotiate: impersonatedAuthentication = ImpersonatedAuthenticationMechanism.Negotiate; break; case PasswordAuthenticationMechanism.Kerberos: impersonatedAuthentication = ImpersonatedAuthenticationMechanism.Kerberos; break; case PasswordAuthenticationMechanism.NtlmDomain: impersonatedAuthentication = ImpersonatedAuthenticationMechanism.NtlmDomain; break; default: ThrowInvalidAuthenticationTypeError(operationName, parameterName, passwordAuthentication); return null; } credentials = new CimCredential(impersonatedAuthentication); } DebugHelper.WriteLogEx("return credential {0}", 1, credentials); return credentials; } #endregion } }