/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Internal;
using Dbg = System.Management.Automation.Diagnostics;
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
namespace Microsoft.PowerShell.Commands
{
#region ConsoleCmdletsBase
///
/// Base class for all the Console related cmdlets.
///
public abstract class ConsoleCmdletsBase : PSCmdlet
{
///
/// Runspace configuration for the current engine
///
///
/// Console cmdlets need object to work with.
///
internal RunspaceConfigForSingleShell Runspace
{
get
{
RunspaceConfigForSingleShell runSpace = Context.RunspaceConfiguration as RunspaceConfigForSingleShell;
return runSpace;
}
}
///
/// InitialSessionState for the current engine
///
internal InitialSessionState InitialSessionState
{
get
{
return Context.InitialSessionState;
}
}
///
/// Throws a terminating error.
///
/// Object which caused this exception.
/// ErrorId for this error.
/// Complete exception object.
/// ErrorCategory for this exception.
internal void ThrowError(
Object targetObject,
string errorId,
Exception innerException,
ErrorCategory category)
{
ThrowTerminatingError(new ErrorRecord(innerException, errorId, category, targetObject));
}
}
#endregion
#region export-console
///
/// Class that implements export-console cmdlet.
///
[Cmdlet(VerbsData.Export, "Console", SupportsShouldProcess = true, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=113298")]
public sealed class ExportConsoleCommand : ConsoleCmdletsBase
{
#region Parameters
///
/// Property that gets/sets console file name.
///
///
/// If a parameter is not supplied then the file represented by $console
/// will be used for saving.
///
[Parameter(Position = 0, Mandatory = false, ValueFromPipeline = true,
ValueFromPipelineByPropertyName = true)]
[Alias("PSPath")]
public string Path
{
get
{
return _fileName;
}
set
{
_fileName = value;
}
}
private string _fileName;
///
/// Property that sets force parameter. This will reset the read-only
/// attribute on an existing file before deleting it.
///
[Parameter()]
public SwitchParameter Force
{
get
{
return _force;
}
set
{
_force = value;
}
}
private bool _force;
///
/// Property that prevents file overwrite.
///
[Parameter()]
[Alias("NoOverwrite")]
public SwitchParameter NoClobber
{
get
{
return _noclobber;
}
set
{
_noclobber = value;
}
}
private bool _noclobber;
#endregion
#region Overrides
///
/// Saves the current console info into a file.
///
protected override void ProcessRecord()
{
// Get filename..
string file = GetFileName();
// if file is null or empty..prompt the user for filename
if (string.IsNullOrEmpty(file))
{
file = PromptUserForFile();
}
// if file is still empty..write error and back out..
if (string.IsNullOrEmpty(file))
{
PSArgumentException ae = PSTraceSource.NewArgumentException("file", ConsoleInfoErrorStrings.FileNameNotResolved);
ThrowError(file, "FileNameNotResolved", ae, ErrorCategory.InvalidArgument);
}
if (WildcardPattern.ContainsWildcardCharacters(file))
{
ThrowError(file, "WildCardNotSupported",
PSTraceSource.NewInvalidOperationException(ConsoleInfoErrorStrings.ConsoleFileWildCardsNotSupported,
file), ErrorCategory.InvalidOperation);
}
// Ofcourse, you cant write to a file from HKLM: etc..
string resolvedPath = ResolveProviderAndPath(file);
// If resolvedPath is empty just return..
if (string.IsNullOrEmpty(resolvedPath))
{
return;
}
// Check whether the file ends with valid extension
if (!resolvedPath.EndsWith(StringLiterals.PowerShellConsoleFileExtension,
StringComparison.OrdinalIgnoreCase))
{
// file does not end with proper extension..create one..
resolvedPath = resolvedPath + StringLiterals.PowerShellConsoleFileExtension;
}
if (!ShouldProcess(this.Path)) // should this be resolvedPath?
return;
//check if destination file exists.
if (File.Exists(resolvedPath))
{
if (NoClobber)
{
string message = StringUtil.Format(
ConsoleInfoErrorStrings.FileExistsNoClobber,
resolvedPath,
"NoClobber"); // prevents localization
Exception uae = new UnauthorizedAccessException(message);
ErrorRecord errorRecord = new ErrorRecord(
uae, "NoClobber", ErrorCategory.ResourceExists, resolvedPath);
// NOTE: this call will throw
ThrowTerminatingError(errorRecord);
}
// Check if the file is read-only
System.IO.FileAttributes attrib = System.IO.File.GetAttributes(resolvedPath);
if ((attrib & System.IO.FileAttributes.ReadOnly) == System.IO.FileAttributes.ReadOnly)
{
if (Force)
{
RemoveFileThrowIfError(resolvedPath);
// Note, we do not attempt to set read-only on the new file
}
else
{
ThrowError(file, "ConsoleFileReadOnly",
PSTraceSource.NewArgumentException(file, ConsoleInfoErrorStrings.ConsoleFileReadOnly, resolvedPath),
ErrorCategory.InvalidArgument);
}
}
}
try
{
if (this.Runspace != null)
{
this.Runspace.SaveAsConsoleFile(resolvedPath);
}
else if (InitialSessionState != null)
{
this.InitialSessionState.SaveAsConsoleFile(resolvedPath);
}
else
{
Dbg.Assert(false, "Both RunspaceConfiguration and InitialSessionState should not be null");
throw PSTraceSource.NewInvalidOperationException(ConsoleInfoErrorStrings.CmdletNotAvailable);
}
}
catch (PSArgumentException mae)
{
ThrowError(resolvedPath,
"PathNotAbsolute", mae, ErrorCategory.InvalidArgument);
}
catch (PSArgumentNullException mane)
{
ThrowError(resolvedPath,
"PathNull", mane, ErrorCategory.InvalidArgument);
}
catch (ArgumentException ae)
{
ThrowError(resolvedPath,
"InvalidCharacetersInPath", ae, ErrorCategory.InvalidArgument);
}
// looks like saving succeeded.
// Now try changing $console
Exception e = null;
try
{
//Update $Console variable
Context.EngineSessionState.SetConsoleVariable();
}
catch (ArgumentNullException ane)
{
e = ane;
}
catch (ArgumentOutOfRangeException aor)
{
e = aor;
}
catch (ArgumentException ae)
{
e = ae;
}
catch (SessionStateUnauthorizedAccessException sue)
{
e = sue;
}
catch (SessionStateOverflowException sof)
{
e = sof;
}
catch (ProviderNotFoundException pnf)
{
e = pnf;
}
catch (System.Management.Automation.DriveNotFoundException dnfe)
{
e = dnfe;
}
catch (NotSupportedException ne)
{
e = ne;
}
catch (ProviderInvocationException pin)
{
e = pin;
}
if (e != null)
{
throw PSTraceSource.NewInvalidOperationException(e,
ConsoleInfoErrorStrings.ConsoleVariableCannotBeSet, resolvedPath);
}
}
#endregion
#region Private Methods
///
/// Removes file specified by destination
///
/// Absolute path of the file to be removed.
private void RemoveFileThrowIfError(string destination)
{
Diagnostics.Assert(System.IO.Path.IsPathRooted(destination),
"RemoveFile expects an absolute path");
System.IO.FileInfo destfile = new System.IO.FileInfo(destination);
if (destfile != null)
{
Exception e = null;
try
{
//Make sure the file is not read only
destfile.Attributes = destfile.Attributes & ~(FileAttributes.ReadOnly | FileAttributes.Hidden);
destfile.Delete();
}
catch (FileNotFoundException fnf)
{
e = fnf;
}
catch (DirectoryNotFoundException dnf)
{
e = dnf;
}
catch (UnauthorizedAccessException uac)
{
e = uac;
}
catch (System.Security.SecurityException se)
{
e = se;
}
catch (ArgumentNullException ane)
{
e = ane;
}
catch (ArgumentException ae)
{
e = ae;
}
catch (PathTooLongException pe)
{
e = pe;
}
catch (NotSupportedException ns)
{
e = ns;
}
catch (IOException ioe)
{
e = ioe;
}
if (e != null)
{
throw PSTraceSource.NewInvalidOperationException(e,
ConsoleInfoErrorStrings.ExportConsoleCannotDeleteFile,
destfile);
}
}
}
///
/// Resolves the specified path and verifies the path belongs to
/// FileSystemProvider.
///
/// Path to resolve
/// A fully qualified string representing filename.
private string ResolveProviderAndPath(string path)
{
// Construct cmdletprovidercontext
CmdletProviderContext cmdContext = new CmdletProviderContext(this);
// First resolve path
PathInfo resolvedPath = ResolvePath(path, true, cmdContext);
// Check whether this is FileSystemProvider..
if (resolvedPath != null)
{
if (resolvedPath.Provider.ImplementingType == typeof(FileSystemProvider))
{
return resolvedPath.Path;
}
throw PSTraceSource.NewInvalidOperationException(ConsoleInfoErrorStrings.ProviderNotSupported, resolvedPath.Provider.Name);
}
return null;
}
///
/// Resolves the specified path to PathInfo objects
///
///
///
/// The path to be resolved. Each path may contain glob characters.
///
///
///
/// If true, resolves the path even if it doesn't exist.
///
///
///
/// The context under which the command is running.
///
///
///
/// A string representing the resolved path.
///
///
private PathInfo ResolvePath(
string pathToResolve,
bool allowNonexistingPaths,
CmdletProviderContext currentCommandContext)
{
Collection results = new Collection();
try
{
// First resolve path
Collection pathInfos =
SessionState.Path.GetResolvedPSPathFromPSPath(
pathToResolve,
currentCommandContext);
foreach (PathInfo pathInfo in pathInfos)
{
results.Add(pathInfo);
}
}
catch (PSNotSupportedException notSupported)
{
WriteError(
new ErrorRecord(
notSupported.ErrorRecord,
notSupported));
}
catch (System.Management.Automation.DriveNotFoundException driveNotFound)
{
WriteError(
new ErrorRecord(
driveNotFound.ErrorRecord,
driveNotFound));
}
catch (ProviderNotFoundException providerNotFound)
{
WriteError(
new ErrorRecord(
providerNotFound.ErrorRecord,
providerNotFound));
}
catch (ItemNotFoundException pathNotFound)
{
if (allowNonexistingPaths)
{
ProviderInfo provider = null;
System.Management.Automation.PSDriveInfo drive = null;
string unresolvedPath =
SessionState.Path.GetUnresolvedProviderPathFromPSPath(
pathToResolve,
currentCommandContext,
out provider,
out drive);
PathInfo pathInfo =
new PathInfo(
drive,
provider,
unresolvedPath,
SessionState);
results.Add(pathInfo);
}
else
{
WriteError(
new ErrorRecord(
pathNotFound.ErrorRecord,
pathNotFound));
}
}
if (results.Count == 1)
{
return results[0];
}
else if (results.Count > 1)
{
Exception e = PSTraceSource.NewNotSupportedException();
WriteError(
new ErrorRecord(e,
"NotSupported",
ErrorCategory.NotImplemented,
results));
return null;
}
else
{
return null;
}
} // ResolvePath
///
/// Gets the filename for the current operation. If Name parameter is empty
/// checks $console. If $console is not present, prompts user?
///
///
/// A string representing filename.
/// If filename cannot be deduced returns null.
///
///
/// 1. $console points to an PSObject that cannot be converted to string.
///
private string GetFileName()
{
if (!string.IsNullOrEmpty(_fileName))
{
// if user specified input..just return
return _fileName;
}
// no input is specified..
// check whether $console is set
PSVariable consoleVariable = Context.SessionState.PSVariable.Get(SpecialVariables.ConsoleFileName);
if (consoleVariable == null)
{
return string.Empty;
}
string consoleValue = consoleVariable.Value as string;
if (consoleValue == null)
{
// $console is not in string format
// Check whether it is in PSObject format
PSObject consolePSObject = consoleVariable.Value as PSObject;
if ((consolePSObject != null) && ((consolePSObject.BaseObject as string) != null))
{
consoleValue = consolePSObject.BaseObject as string;
}
}
if (consoleValue != null)
{
// ConsoleFileName variable is found..
return consoleValue;
}
throw PSTraceSource.NewArgumentException("fileName", ConsoleInfoErrorStrings.ConsoleCannotbeConvertedToString);
}
///
/// Prompt user for filename.
///
///
/// User input in string format.
/// If user chooses not to export, an empty string is returned.
///
/// No exception is thrown
private string PromptUserForFile()
{
// ask user what to do..
if (ShouldContinue(
ConsoleInfoErrorStrings.PromptForExportConsole,
null))
{
string caption = StringUtil.Format(ConsoleInfoErrorStrings.FileNameCaptionForExportConsole, "export-console");
string message = ConsoleInfoErrorStrings.FileNamePromptMessage;
// Construct a field description object of required parameters
Collection desc = new Collection();
desc.Add(new System.Management.Automation.Host.FieldDescription("Name"));
// get user input from the host
System.Collections.Generic.Dictionary returnValue =
this.PSHostInternal.UI.Prompt(caption, message, desc);
if ((returnValue != null) && (returnValue["Name"] != null))
{
return (returnValue["Name"].BaseObject as string);
}
// We dont have any input..
return string.Empty;
}
// If user chooses not to export, return empty string.
return string.Empty;
}
#endregion
}
#endregion
}