diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index fa2c47c029..5c0f00112f 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -4595,7 +4595,12 @@ $RawUI.SetBufferContents( else { // Porting note: non-Windows platforms use `clear` - return "& (Get-Command -CommandType Application clear | Select-Object -First 1).Definition"; + return @" +& (Get-Command -CommandType Application clear | Select-Object -First 1).Definition +# .Link +# https://go.microsoft.com/fwlink/?LinkID=225747 +# .ExternalHelp System.Management.Automation.dll-help.xml +"; } } diff --git a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs index ce891c121b..7b590ff0c5 100644 --- a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs +++ b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs @@ -229,7 +229,7 @@ namespace Microsoft.PowerShell.Commands } return; } - if (!module.HelpInfoUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) + if (!(module.HelpInfoUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || module.HelpInfoUri.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) { if (!noErrors) { diff --git a/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs b/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs index 025c36eac8..8abd2df282 100644 --- a/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs +++ b/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs @@ -12,7 +12,11 @@ namespace System.Management.Automation.Help /// internal class UpdatableHelpModuleInfo { +#if UNIX + internal static readonly string HelpContentZipName = "HelpContent.zip"; +#else internal static readonly string HelpContentZipName = "HelpContent.cab"; +#endif internal static readonly string HelpIntoXmlName = "HelpInfo.xml"; /// diff --git a/src/System.Management.Automation/help/UpdatableHelpSystem.cs b/src/System.Management.Automation/help/UpdatableHelpSystem.cs index a8aec1a9c7..90fcde70ab 100644 --- a/src/System.Management.Automation/help/UpdatableHelpSystem.cs +++ b/src/System.Management.Automation/help/UpdatableHelpSystem.cs @@ -17,8 +17,8 @@ using System.Text; using Microsoft.PowerShell.Commands; using Microsoft.Win32; using System.Security; - #if CORECLR +using System.IO.Compression; using System.Net.Http; using System.Threading.Tasks; // Use stub for SerializableAttribute, SerializationInfo and ISerializable related types. WebClient. @@ -1209,9 +1209,9 @@ namespace System.Management.Automation.Help OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format( HelpDisplayStrings.UpdateProgressInstalling), 0)); - string combinedSourcePath = Path.Combine(sourcePath, fileName); + string combinedSourcePath = GetFilePath(Path.Combine(sourcePath, fileName)); - if (!File.Exists(combinedSourcePath)) + if (string.IsNullOrEmpty(combinedSourcePath)) { throw new UpdatableHelpSystemException("HelpContentNotFound", StringUtil.Format(HelpDisplayStrings.HelpContentNotFound), ErrorCategory.ResourceUnavailable, null, null); @@ -1249,6 +1249,37 @@ namespace System.Management.Automation.Help } } +#if UNIX + private bool ExpandArchive(string source, string destination) + { + bool sucessfulDecompression = false; + + try + { + using (FileStream archiveFileStream = new FileStream(source, IO.FileMode.Open, FileAccess.Read)) + using (ZipArchive zipArchive = new ZipArchive(archiveFileStream, ZipArchiveMode.Read, false)) + { + foreach (ZipArchiveEntry entry in zipArchive.Entries) + { + string extractPath = Path.Combine(destination, entry.FullName); + entry.ExtractToFile(extractPath); + } + sucessfulDecompression = true; + } + } + catch (ArgumentException) { } + catch (InvalidDataException) { } + catch (NotSupportedException) { } + catch (FileNotFoundException) { } + catch (IOException) { } + catch (SecurityException) { } + catch (UnauthorizedAccessException) { } + catch (ObjectDisposedException) { } + + return sucessfulDecompression; + } +#endif + /// /// Unzips to help content to a given location /// @@ -1265,20 +1296,25 @@ namespace System.Management.Automation.Help Directory.CreateDirectory(destPath); } - string cabDir = Path.GetDirectoryName(srcPath); - + string sourceDirectory = Path.GetDirectoryName(srcPath); + bool sucessfulDecompression = false; +#if UNIX + sucessfulDecompression = ExpandArchive(Path.Combine(sourceDirectory, Path.GetFileName(srcPath)), destPath); +#else // Cabinet API doesn't handle the trailing back slash - if (!cabDir.EndsWith("\\", StringComparison.Ordinal)) + if (!sourceDirectory.EndsWith("\\", StringComparison.Ordinal)) { - cabDir += "\\"; + sourceDirectory += "\\"; } if (!destPath.EndsWith("\\", StringComparison.Ordinal)) { destPath += "\\"; } - - if (!CabinetExtractorFactory.GetCabinetExtractor().Extract(Path.GetFileName(srcPath), cabDir, destPath)) + + sucessfulDecompression = CabinetExtractorFactory.GetCabinetExtractor().Extract(Path.GetFileName(srcPath), sourceDirectory, destPath); +#endif + if (!sucessfulDecompression) { throw new UpdatableHelpSystemException("UnableToExtract", StringUtil.Format(HelpDisplayStrings.UnzipFailure), ErrorCategory.InvalidOperation, null, null); @@ -1574,9 +1610,10 @@ namespace System.Management.Automation.Help } } - if (File.Exists(path)) + string filePath = GetFilePath(path); + if (!string.IsNullOrEmpty(filePath)) { - using (FileStream currentHelpInfoFile = new FileStream(path, FileMode.Open, FileAccess.Read)) + using (FileStream currentHelpInfoFile = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { StreamReader reader = new StreamReader(currentHelpInfoFile); @@ -1587,6 +1624,43 @@ namespace System.Management.Automation.Help return null; } + /// + /// Validate the given path. If it exists, return the full path to the file. + /// + /// + /// + internal static string GetFilePath(string path) + { +#if UNIX + // On Linux, file paths are case sensitive. + // The user does not have control over the files (HelpInfo.xml, .zip, and cab) that are generated by the Publishing team. + // The logic below is to support updating help content via sourcepath parameter for case insensitive files. + FileInfo item = new FileInfo(path); + string directoryPath = item.Directory.FullName; + string fileName = item.Name; + + // Prerequisite: The directory in the given path must exist and it is case sensitive. + if (Utils.NativeDirectoryExists(directoryPath)) + { + // Get the list of files in the directory. + string[] fileList = Directory.GetFiles(directoryPath); + foreach (string filePath in fileList) + { + if (filePath.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)) + { + return filePath; + } + } + } +#else + if (Utils.NativeFileExists(path)) + { + return path; + } +#endif + return null; + } + /// /// Gets the default source path from GP /// diff --git a/src/System.Management.Automation/project.json b/src/System.Management.Automation/project.json index f2859ffce8..a84a540170 100644 --- a/src/System.Management.Automation/project.json +++ b/src/System.Management.Automation/project.json @@ -161,6 +161,7 @@ "System.IO.FileSystem.DriveInfo": "4.3.0-preview1-24530-04", "System.IO.FileSystem.Watcher": "4.3.0-preview1-24530-04", "System.IO.Pipes": "4.3.0-preview1-24530-04", + "System.IO.Compression.ZipFile": "4.3.0-preview1-24530-04", "System.Linq.Expressions": "4.3.0-preview1-24530-04", "System.Net.Http": "4.3.0-preview1-24530-04", "System.Net.NetworkInformation": "4.3.0-preview1-24530-04",