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