// 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
}
}