Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
UpdatableHelpSystem.cs1655 linesDownload Raw Back to help
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.ComponentModel;7using System.Diagnostics;8using System.Globalization;9using System.IO;10using System.IO.Compression;11using System.Management.Automation.Configuration;12using System.Management.Automation.Internal;13using System.Net;14using System.Net.Http;15using System.Runtime.Serialization;16using System.Security;17using System.Text;18using System.Threading;19using System.Threading.Tasks;20using System.Xml;21using System.Xml.Schema;22 23using Microsoft.PowerShell.Commands;24using Microsoft.Win32;25 26namespace System.Management.Automation.Help27{28    /// <summary>29    /// Updatable help system exception.30    /// </summary>31    internal class UpdatableHelpSystemException : Exception32    {33        /// <summary>34        /// Class constructor.35        /// </summary>36        /// <param name="errorId">FullyQualifiedErrorId.</param>37        /// <param name="message">Exception message.</param>38        /// <param name="cat">Category.</param>39        /// <param name="targetObject">Target object.</param>40        /// <param name="innerException">Inner exception.</param>41        internal UpdatableHelpSystemException(string errorId, string message, ErrorCategory cat, object targetObject, Exception innerException)42            : base(message, innerException)43        {44            FullyQualifiedErrorId = errorId;45            ErrorCategory = cat;46            TargetObject = targetObject;47        }48 49#if !CORECLR50        /// <summary>51        /// Class constructor.52        /// </summary>53        /// <param name="serializationInfo">Serialization info.</param>54        /// <param name="streamingContext">Streaming context.</param>55        protected UpdatableHelpSystemException(SerializationInfo serializationInfo, StreamingContext streamingContext)56            : base(serializationInfo, streamingContext)57        {58        }59#endif60 61        /// <summary>62        /// Fully qualified error id.63        /// </summary>64        internal string FullyQualifiedErrorId { get; }65 66        /// <summary>67        /// Error category.68        /// </summary>69        internal ErrorCategory ErrorCategory { get; }70 71        /// <summary>72        /// Target object.73        /// </summary>74        internal object TargetObject { get; }75    }76 77    /// <summary>78    /// Exception context.79    /// </summary>80    internal class UpdatableHelpExceptionContext81    {82        /// <summary>83        /// Class constructor.84        /// </summary>85        /// <param name="exception">Exception to wrap.</param>86        internal UpdatableHelpExceptionContext(UpdatableHelpSystemException exception)87        {88            Exception = exception;89            Modules = new HashSet<string>(StringComparer.OrdinalIgnoreCase);90            Cultures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);91        }92 93        /// <summary>94        /// A list of modules.95        /// </summary>96        internal HashSet<string> Modules { get; set; }97 98        /// <summary>99        /// A list of UI cultures.100        /// </summary>101        internal HashSet<string> Cultures { get; set; }102 103        /// <summary>104        /// Gets the help system exception.105        /// </summary>106        internal UpdatableHelpSystemException Exception { get; }107 108        /// <summary>109        /// Creates an error record from this context.110        /// </summary>111        /// <param name="commandType">Command type.</param>112        /// <returns>Error record.</returns>113        internal ErrorRecord CreateErrorRecord(UpdatableHelpCommandType commandType)114        {115            Debug.Assert(Modules.Count != 0);116 117            return new ErrorRecord(new Exception(GetExceptionMessage(commandType)), Exception.FullyQualifiedErrorId, Exception.ErrorCategory,118                Exception.TargetObject);119        }120 121        /// <summary>122        /// Gets the exception message.123        /// </summary>124        /// <param name="commandType"></param>125        /// <returns></returns>126        internal string GetExceptionMessage(UpdatableHelpCommandType commandType)127        {128            string message = string.Empty;129            SortedSet<string> sortedModules = new SortedSet<string>(Modules, StringComparer.CurrentCultureIgnoreCase);130            SortedSet<string> sortedCultures = new SortedSet<string>(Cultures, StringComparer.CurrentCultureIgnoreCase);131            string modules = string.Join(", ", sortedModules);132            string cultures = string.Join(", ", sortedCultures);133 134            if (commandType == UpdatableHelpCommandType.UpdateHelpCommand)135            {136                if (Cultures.Count == 0)137                {138                    message = StringUtil.Format(HelpDisplayStrings.FailedToUpdateHelpForModule, modules, Exception.Message);139                }140                else141                {142                    message = StringUtil.Format(HelpDisplayStrings.FailedToUpdateHelpForModuleWithCulture, modules, cultures, Exception.Message);143                }144            }145            else146            {147                if (Cultures.Count == 0)148                {149                    message = StringUtil.Format(HelpDisplayStrings.FailedToSaveHelpForModule, modules, Exception.Message);150                }151                else152                {153                    message = StringUtil.Format(HelpDisplayStrings.FailedToSaveHelpForModuleWithCulture, modules, cultures, Exception.Message);154                }155            }156 157            return message;158        }159    }160 161    /// <summary>162    /// Enumeration showing Update or Save help.163    /// </summary>164    internal enum UpdatableHelpCommandType165    {166        UnknownCommand = 0,167        UpdateHelpCommand = 1,168        SaveHelpCommand = 2169    }170 171    /// <summary>172    /// Progress event arguments.173    /// </summary>174    internal class UpdatableHelpProgressEventArgs : EventArgs175    {176        /// <summary>177        /// Class constructor.178        /// </summary>179        /// <param name="moduleName">Module name.</param>180        /// <param name="status">Progress status.</param>181        /// <param name="percent">Progress percentage.</param>182        internal UpdatableHelpProgressEventArgs(string moduleName, string status, int percent)183        {184            Debug.Assert(!string.IsNullOrEmpty(status));185 186            CommandType = UpdatableHelpCommandType.UnknownCommand;187            ProgressStatus = status;188            ProgressPercent = percent;189            ModuleName = moduleName;190        }191 192        /// <summary>193        /// Class constructor.194        /// </summary>195        /// <param name="moduleName">Module name.</param>196        /// <param name="type">Command type.</param>197        /// <param name="status">Progress status.</param>198        /// <param name="percent">Progress percentage.</param>199        internal UpdatableHelpProgressEventArgs(string moduleName, UpdatableHelpCommandType type, string status, int percent)200        {201            Debug.Assert(!string.IsNullOrEmpty(status));202 203            CommandType = type;204            ProgressStatus = status;205            ProgressPercent = percent;206            ModuleName = moduleName;207        }208 209        /// <summary>210        /// Progress status.211        /// </summary>212        internal string ProgressStatus { get; }213 214        /// <summary>215        /// Progress percentage.216        /// </summary>217        internal int ProgressPercent { get; }218 219        /// <summary>220        /// Module name.221        /// </summary>222        internal string ModuleName { get; }223 224        /// <summary>225        /// Command type.226        /// </summary>227        internal UpdatableHelpCommandType CommandType { get; set; }228    }229 230    /// <summary>231    /// This class implements the Updatable Help System common operations.232    /// </summary>233    internal class UpdatableHelpSystem : IDisposable234    {235        private readonly TimeSpan _defaultTimeout;236        private readonly Collection<UpdatableHelpProgressEventArgs> _progressEvents;237        private bool _stopping;238        private readonly object _syncObject;239        private readonly UpdatableHelpCommandBase _cmdlet;240        private readonly CancellationTokenSource _cancelTokenSource;241 242        internal HttpClient HttpClient { get; }243 244        internal bool UseDefaultCredentials;245 246        internal string CurrentModule { get; set; }247 248        /// <summary>249        /// Class constructor.250        /// </summary>251        internal UpdatableHelpSystem(UpdatableHelpCommandBase cmdlet, bool useDefaultCredentials)252        {253            HttpClient = new HttpClient();254            _defaultTimeout = new TimeSpan(0, 0, 30);255            _progressEvents = new Collection<UpdatableHelpProgressEventArgs>();256            Errors = new Collection<Exception>();257            _stopping = false;258            _syncObject = new object();259            _cmdlet = cmdlet;260            _cancelTokenSource = new CancellationTokenSource();261 262            UseDefaultCredentials = useDefaultCredentials;263 264#if !CORECLR265            WebClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(HandleDownloadProgressChanged);266            WebClient.DownloadFileCompleted += new AsyncCompletedEventHandler(HandleDownloadFileCompleted);267#endif268        }269 270        /// <summary>271        /// Disposes the help system.272        /// </summary>273        public void Dispose()274        {275#if !CORECLR276            _completionEvent.Dispose();277#endif278            _cancelTokenSource.Dispose();279            HttpClient.Dispose();280            GC.SuppressFinalize(this);281        }282 283        /// <summary>284        /// Help system errors.285        /// </summary>286        internal Collection<Exception> Errors { get; }287 288        /// <summary>289        /// Gets the current UIculture (includes the fallback chain)290        /// </summary>291        /// <returns>A list of cultures.</returns>292        internal IEnumerable<string> GetCurrentUICulture()293        {294            CultureInfo culture = CultureInfo.CurrentUICulture;295 296            // Allow tests to override system culture297            if (InternalTestHooks.CurrentUICulture != null)298            {299                culture = InternalTestHooks.CurrentUICulture;300            }301 302            return CultureSpecificUpdatableHelp.GetCultureFallbackChain(culture);303        }304 305        #region Help Metadata Retrieval306 307        /// <summary>308        /// Gets an internal help URI.309        /// </summary>310        /// <param name="module">Internal module information.</param>311        /// <param name="culture">Help content culture.</param>312        /// <returns>Internal help uri representation.</returns>313        internal UpdatableHelpUri GetHelpInfoUri(UpdatableHelpModuleInfo module, CultureInfo culture)314        {315            return new UpdatableHelpUri(module.ModuleName, module.ModuleGuid, culture, ResolveUri(module.HelpInfoUri, false));316        }317 318        /// <summary>319        /// Gets the HelpInfo xml from the given URI.320        /// </summary>321        /// <param name="commandType">Command type.</param>322        /// <param name="uri">HelpInfo URI.</param>323        /// <param name="moduleName">Module name.</param>324        /// <param name="moduleGuid">Module GUID.</param>325        /// <param name="culture">Current UI culture.</param>326        /// <returns>HelpInfo object.</returns>327        internal UpdatableHelpInfo GetHelpInfo(UpdatableHelpCommandType commandType, string uri, string moduleName, Guid moduleGuid, string culture)328        {329            try330            {331                OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format(332                    HelpDisplayStrings.UpdateProgressLocating), 0));333 334                string xml;335                using (HttpClientHandler handler = new HttpClientHandler())336                {337                    handler.UseDefaultCredentials = UseDefaultCredentials;338                    using (HttpClient client = new HttpClient(handler))339                    {340                        client.Timeout = _defaultTimeout;341                        Task<string> responseBody = client.GetStringAsync(uri);342                        xml = responseBody.Result;343                        if (responseBody.Exception != null)344                        {345                            return null;346                        }347                    }348                }349 350                UpdatableHelpInfo helpInfo = CreateHelpInfo(xml, moduleName, moduleGuid,351                                                            currentCulture: culture, pathOverride: null, verbose: true,352                                                            shouldResolveUri: true, ignoreValidationException: false);353 354                return helpInfo;355            }356#if !CORECLR357            catch (WebException)358            {359                return null;360            }361#endif362            finally363            {364                OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format(365                    HelpDisplayStrings.UpdateProgressLocating), 100));366            }367        }368 369        /// <summary>370        /// Sends a standard HTTP request to get the resolved URI (potential FwLinks)371        /// </summary>372        /// <param name="baseUri">Base URI.</param>373        /// <param name="verbose"></param>374        /// <returns>Resolved URI.</returns>375        private string ResolveUri(string baseUri, bool verbose)376        {377            Debug.Assert(!string.IsNullOrEmpty(baseUri));378 379            // Directory.Exists checks if baseUri is a network drive or380            // a local directory. If baseUri is local, we don't need to resolve it.381            //382            // The / check works because all of our fwlinks must resolve383            // to a remote virtual directory. I think HTTP always appends /384            // in reference to a directory.385            // Like if you send a request to www.technet.com/powershell you will get386            // a 301/203 response with the response URI set to www.technet.com/powershell/387            //388            if (Directory.Exists(baseUri) || baseUri.EndsWith('/'))389            {390                if (verbose)391                {392                    _cmdlet.WriteVerbose(StringUtil.Format(RemotingErrorIdStrings.URIRedirectWarningToHost, baseUri));393                }394 395                return baseUri;396            }397 398            if (verbose)399            {400                _cmdlet.WriteVerbose(StringUtil.Format(HelpDisplayStrings.UpdateHelpResolveUriVerbose, baseUri));401            }402 403            string uri = baseUri;404 405            try406            {407                // We only allow 10 redirections408                for (int i = 0; i < 10; i++)409                {410                    if (_stopping)411                    {412                        return uri;413                    }414 415                    using (HttpClientHandler handler = new HttpClientHandler())416                    {417                        handler.AllowAutoRedirect = false;418                        handler.UseDefaultCredentials = UseDefaultCredentials;419                        using (HttpClient client = new HttpClient(handler))420                        {421                            client.Timeout = new TimeSpan(0, 0, 30); // Set 30 second timeout422                            // codeql[cs/ssrf] - This is expected Poweshell behavior and the user assumes trust for the module they download and any URIs it references. The URIs are also not executables or scripts that would be invoked by this method.423                            Task<HttpResponseMessage> responseMessage = client.GetAsync(uri);424                            using (HttpResponseMessage response = responseMessage.Result)425                            {426                                if (response.StatusCode == HttpStatusCode.Found ||427                                    response.StatusCode == HttpStatusCode.Redirect ||428                                    response.StatusCode == HttpStatusCode.Moved ||429                                    response.StatusCode == HttpStatusCode.MovedPermanently)430                                {431                                    Uri responseUri = response.Headers.Location;432 433                                    if (responseUri.IsAbsoluteUri)434                                    {435                                        uri = responseUri.ToString();436                                    }437                                    else438                                    {439                                        Uri originalAbs = new Uri(uri);440                                        uri = uri.Replace(originalAbs.AbsolutePath, responseUri.ToString());441                                    }442 443                                    uri = uri.Trim();444 445                                    if (verbose)446                                    {447                                        _cmdlet.WriteVerbose(StringUtil.Format(RemotingErrorIdStrings.URIRedirectWarningToHost, uri));448                                    }449 450                                    if (uri.EndsWith('/'))451                                    {452                                        return uri;453                                    }454                                }455                                else if (response.StatusCode == HttpStatusCode.OK)456                                {457                                    if (uri.EndsWith('/'))458                                    {459                                        return uri;460                                    }461                                    else462                                    {463                                        throw new UpdatableHelpSystemException("InvalidHelpInfoUri", StringUtil.Format(HelpDisplayStrings.InvalidHelpInfoUri, uri),464                                            ErrorCategory.InvalidOperation, null, null);465                                    }466                                }467                            }468                        }469                    }470                }471            }472            catch (UriFormatException e)473            {474                throw new UpdatableHelpSystemException("InvalidUriFormat", e.Message, ErrorCategory.InvalidData, null, e);475            }476 477            throw new UpdatableHelpSystemException("TooManyRedirections", StringUtil.Format(HelpDisplayStrings.TooManyRedirections),478                ErrorCategory.InvalidOperation, null, null);479        }480 481        /// <summary>482        /// HelpInfo.xml schema.483        /// </summary>484        private const string HelpInfoXmlSchema = @"<?xml version=""1.0"" encoding=""utf-8""?>485            <xs:schema attributeFormDefault=""unqualified"" elementFormDefault=""qualified""486                targetNamespace=""http://schemas.microsoft.com/powershell/help/2010/05"" xmlns:xs=""http://www.w3.org/2001/XMLSchema"">487                <xs:element name=""HelpInfo"">488                    <xs:complexType>489                        <xs:sequence>490                            <xs:element name=""HelpContentURI"" type=""xs:anyURI"" minOccurs=""1"" maxOccurs=""1"" />491                            <xs:element name=""SupportedUICultures"" minOccurs=""1"" maxOccurs=""1"">492                                <xs:complexType>493                                    <xs:sequence>494                                        <xs:element name=""UICulture"" minOccurs=""1"" maxOccurs=""unbounded"">495                                            <xs:complexType>496                                                <xs:sequence>497                                                    <xs:element name=""UICultureName"" type=""xs:language"" minOccurs=""1"" maxOccurs=""1"" />498                                                    <xs:element name=""UICultureVersion"" type=""xs:string"" minOccurs=""1"" maxOccurs=""1"" />499                                                </xs:sequence>500                                            </xs:complexType>501                                        </xs:element>502                                    </xs:sequence>503                                </xs:complexType>504                            </xs:element>505                        </xs:sequence>506                    </xs:complexType>507                </xs:element>508            </xs:schema>";509 510        private const string HelpInfoXmlNamespace = "http://schemas.microsoft.com/powershell/help/2010/05";511        private const string HelpInfoXmlValidationFailure = "HelpInfoXmlValidationFailure";512        private const string MamlXmlNamespace = "http://schemas.microsoft.com/maml/2004/10";513        private const string CommandXmlNamespace = "http://schemas.microsoft.com/maml/dev/command/2004/10";514        private const string DscResourceXmlNamespace = "http://schemas.microsoft.com/maml/dev/dscResource/2004/10";515 516        /// <summary>517        /// Creates a HelpInfo object.518        /// </summary>519        /// <param name="xml">XML text.</param>520        /// <param name="moduleName">Module name.</param>521        /// <param name="moduleGuid">Module GUID.</param>522        /// <param name="currentCulture">Current UI cultures.</param>523        /// <param name="pathOverride">Overrides the path contained within HelpInfo.xml.</param>524        /// <param name="verbose"></param>525        /// <param name="shouldResolveUri">526        /// Resolve the uri retrieved from the <paramref name="xml"/> content. The uri is resolved527        /// to handle redirections if any.528        /// </param>529        /// <param name="ignoreValidationException">Ignore the xsd validation exception and return null in such case.</param>530        /// <returns>HelpInfo object.</returns>531        internal UpdatableHelpInfo CreateHelpInfo(string xml, string moduleName, Guid moduleGuid,532            string currentCulture, string pathOverride, bool verbose, bool shouldResolveUri, bool ignoreValidationException)533        {534            XmlDocument document = null;535            try536            {537                document = CreateValidXmlDocument(xml, HelpInfoXmlNamespace, HelpInfoXmlSchema,538                    new ValidationEventHandler(HelpInfoValidationHandler),539                    true);540            }541            catch (UpdatableHelpSystemException e)542            {543                if (ignoreValidationException && HelpInfoXmlValidationFailure.Equals(e.FullyQualifiedErrorId, StringComparison.Ordinal))544                {545                    return null;546                }547 548                throw;549            }550            catch (XmlException e)551            {552                if (ignoreValidationException)553                {554                    return null;555                }556 557                throw new UpdatableHelpSystemException(HelpInfoXmlValidationFailure,558                    e.Message, ErrorCategory.InvalidData, null, e);559            }560 561            string uri = pathOverride;562            string unresolvedUri = document["HelpInfo"]["HelpContentURI"].InnerText;563 564            if (string.IsNullOrEmpty(pathOverride))565            {566                if (shouldResolveUri)567                {568                    uri = ResolveUri(unresolvedUri, verbose);569                }570                else571                {572                    uri = unresolvedUri;573                }574            }575 576            XmlNodeList cultures = document["HelpInfo"]["SupportedUICultures"].ChildNodes;577 578            CultureSpecificUpdatableHelp[] updatableHelpItem = new CultureSpecificUpdatableHelp[cultures.Count];579 580            for (int i = 0; i < cultures.Count; i++)581            {582                updatableHelpItem[i] = new CultureSpecificUpdatableHelp(583                    new CultureInfo(cultures[i]["UICultureName"].InnerText),584                    new Version(cultures[i]["UICultureVersion"].InnerText));585            }586 587            UpdatableHelpInfo helpInfo = new UpdatableHelpInfo(unresolvedUri, updatableHelpItem);588 589            if (!string.IsNullOrEmpty(currentCulture))590            {591                for (int i = 0; i < updatableHelpItem.Length; i++)592                {593                    if (updatableHelpItem[i].IsCultureSupported(currentCulture))594                    {595                        helpInfo.HelpContentUriCollection.Add(new UpdatableHelpUri(moduleName, moduleGuid, updatableHelpItem[i].Culture, uri));596                    }597                }598            }599 600            if (!string.IsNullOrEmpty(currentCulture) && helpInfo.HelpContentUriCollection.Count == 0)601            {602                // throw exception603                throw new UpdatableHelpSystemException("HelpCultureNotSupported",604                    StringUtil.Format(HelpDisplayStrings.HelpCultureNotSupported,605                        currentCulture, helpInfo.GetSupportedCultures()), ErrorCategory.InvalidOperation, null, null);606            }607 608            return helpInfo;609        }610 611        /// <summary>612        /// Creates a valid xml document.613        /// </summary>614        /// <param name="xml">Input xml.</param>615        /// <param name="ns">Schema namespace.</param>616        /// <param name="schema">Xml schema.</param>617        /// <param name="handler">Validation event handler.</param>618        /// <param name="helpInfo">HelpInfo or HelpContent?</param>619        private static XmlDocument CreateValidXmlDocument(string xml, string ns, string schema, ValidationEventHandler handler,620            bool helpInfo)621        {622            XmlReaderSettings settings = new XmlReaderSettings();623 624            settings.Schemas.Add(ns, new XmlTextReader(new StringReader(schema)));625            settings.ValidationType = ValidationType.Schema;626 627            XmlReader reader = XmlReader.Create(new StringReader(xml), settings);628            XmlDocument document = new XmlDocument();629 630            try631            {632                document.Load(reader);633                document.Validate(handler);634            }635            catch (XmlSchemaValidationException e)636            {637                if (helpInfo)638                {639                    throw new UpdatableHelpSystemException(HelpInfoXmlValidationFailure,640                        StringUtil.Format(HelpDisplayStrings.HelpInfoXmlValidationFailure, e.Message),641                        ErrorCategory.InvalidData, null, e);642                }643                else644                {645                    throw new UpdatableHelpSystemException("HelpContentXmlValidationFailure",646                        StringUtil.Format(HelpDisplayStrings.HelpContentXmlValidationFailure, e.Message),647                        ErrorCategory.InvalidData, null, e);648                }649            }650 651            return document;652        }653 654        /// <summary>655        /// Handles HelpInfo XML validation events.656        /// </summary>657        /// <param name="sender">Event sender.</param>658        /// <param name="arg">Event arguments.</param>659        private void HelpInfoValidationHandler(object sender, ValidationEventArgs arg)660        {661            switch (arg.Severity)662            {663                case XmlSeverityType.Error:664                    {665                        throw new UpdatableHelpSystemException(HelpInfoXmlValidationFailure,666                            StringUtil.Format(HelpDisplayStrings.HelpInfoXmlValidationFailure),667                            ErrorCategory.InvalidData, null, arg.Exception);668                    }669                case XmlSeverityType.Warning:670                    break;671            }672        }673 674        /// <summary>675        /// Handles Help content MAML validation events.676        /// </summary>677        /// <param name="sender">Event sender.</param>678        /// <param name="arg">Event arguments.</param>679        private void HelpContentValidationHandler(object sender, ValidationEventArgs arg)680        {681            switch (arg.Severity)682            {683                case XmlSeverityType.Error:684                    {685                        throw new UpdatableHelpSystemException("HelpContentXmlValidationFailure",686                            StringUtil.Format(HelpDisplayStrings.HelpContentXmlValidationFailure),687                            ErrorCategory.InvalidData, null, arg.Exception);688                    }689                case XmlSeverityType.Warning:690                    break;691            }692        }693 694        #endregion695 696        #region Help Content Retrieval697 698        /// <summary>699        /// Cancels all asynchronous download operations.700        /// </summary>701        internal void CancelDownload()702        {703            _cancelTokenSource.Cancel();704            _stopping = true;705        }706 707        /// <summary>708        /// Downloads and installs help content.709        /// </summary>710        /// <param name="commandType">Command type.</param>711        /// <param name="context">Execution context.</param>712        /// <param name="destPaths">Destination paths.</param>713        /// <param name="fileName">File names.</param>714        /// <param name="culture">Culture to update.</param>715        /// <param name="helpContentUri">Help content uri.</param>716        /// <param name="xsdPath">Path of the maml XSDs.</param>717        /// <param name="installed">Files installed.</param>718        /// <returns>True if the operation succeeded, false if not.</returns>719        internal bool DownloadAndInstallHelpContent(UpdatableHelpCommandType commandType, ExecutionContext context, Collection<string> destPaths,720            string fileName, CultureInfo culture, string helpContentUri, string xsdPath, out Collection<string> installed)721        {722            if (_stopping)723            {724                installed = new Collection<string>();725                return false;726            }727 728            string cache = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));729 730            if (!DownloadHelpContent(commandType, cache, helpContentUri, fileName, culture.Name))731            {732                installed = new Collection<string>();733                return false;734            }735 736            InstallHelpContent(commandType, context, cache, destPaths, fileName, cache, culture, xsdPath, out installed);737 738            return true;739        }740 741        /// <summary>742        /// Downloads the help content.743        /// </summary>744        /// <param name="commandType">Command type.</param>745        /// <param name="path">Destination path.</param>746        /// <param name="helpContentUri">Help content uri.</param>747        /// <param name="fileName">Combined file name.</param>748        /// <param name="culture">Culture name.</param>749        /// <returns>True if the operation succeeded, false if not.</returns>750        internal bool DownloadHelpContent(UpdatableHelpCommandType commandType, string path, string helpContentUri, string fileName, string culture)751        {752            if (_stopping)753            {754                return false;755            }756 757            if (!Directory.Exists(path))758            {759                Directory.CreateDirectory(path);760            }761 762            OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format(763                HelpDisplayStrings.UpdateProgressConnecting), 0));764 765            string uri = helpContentUri + fileName;766 767            return DownloadHelpContentHttpClient(uri, Path.Combine(path, fileName), commandType);768        }769 770        /// <summary>771        /// Downloads the help content and saves it to a directory.772        /// </summary>773        /// <param name="uri"></param>774        /// <param name="fileName"></param>775        /// <param name="commandType"></param>776        /// <returns></returns>777        private bool DownloadHelpContentHttpClient(string uri, string fileName, UpdatableHelpCommandType commandType)778        {779            // TODO: Was it intentional for them to remove IDisposable from Task?780            using (HttpClientHandler handler = new HttpClientHandler())781            {782                handler.AllowAutoRedirect = false;783                handler.UseDefaultCredentials = UseDefaultCredentials;784                using (HttpClient client = new HttpClient(handler))785                {786                    client.Timeout = _defaultTimeout;787                    // codeql[cs/ssrf] - This is expected Poweshell behavior and the user assumes trust for the module they download and any URIs it references. The URIs are also not executables or scripts that would be invoked by this method.788                    Task<HttpResponseMessage> responseMsg = client.GetAsync(new Uri(uri), _cancelTokenSource.Token);789 790                    // TODO: Should I use a continuation to write the stream to a file?791                    responseMsg.Wait();792 793                    if (_stopping)794                    {795                        return true;796                    }797 798                    if (!responseMsg.IsCanceled)799                    {800                        if (responseMsg.Exception != null)801                        {802                            Errors.Add(new UpdatableHelpSystemException("HelpContentNotFound",803                                StringUtil.Format(HelpDisplayStrings.HelpContentNotFound),804                                ErrorCategory.ResourceUnavailable, null, responseMsg.Exception));805                        }806                        else807                        {808                            lock (_syncObject)809                            {810                                _progressEvents.Add(new UpdatableHelpProgressEventArgs(CurrentModule, StringUtil.Format(811                                    HelpDisplayStrings.UpdateProgressDownloading), 100));812                            }813 814                            // Write the stream to the specified file to achieve functional parity with WebClient.DownloadFileAsync().815                            HttpResponseMessage response = responseMsg.Result;816                            if (response.IsSuccessStatusCode)817                            {818                                WriteResponseToFile(response, fileName);819                            }820                            else821                            {822                                Errors.Add(new UpdatableHelpSystemException("HelpContentNotFound",823                                    StringUtil.Format(HelpDisplayStrings.HelpContentNotFound),824                                    ErrorCategory.ResourceUnavailable, null, responseMsg.Exception));825                            }826                        }827                    }828 829                    SendProgressEvents(commandType);830                }831            }832 833            return (Errors.Count == 0);834        }835 836        /// <summary>837        /// Writes the content of an HTTP response to the specified file.838        /// </summary>839        /// <param name="response"></param>840        /// <param name="fileName"></param>841        private void WriteResponseToFile(HttpResponseMessage response, string fileName)842        {843            // TODO: Settings to use? FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite844            using (FileStream downloadedFileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write))845            {846                Task copyStreamOp = response.Content.CopyToAsync(downloadedFileStream);847                copyStreamOp.Wait();848                if (copyStreamOp.Exception != null)849                {850                    Errors.Add(copyStreamOp.Exception);851                }852            }853        }854 855        private void SendProgressEvents(UpdatableHelpCommandType commandType)856        {857            // Send progress events858            lock (_syncObject)859            {860                if (_progressEvents.Count > 0)861                {862                    foreach (UpdatableHelpProgressEventArgs evt in _progressEvents)863                    {864                        evt.CommandType = commandType;865 866                        OnProgressChanged(this, evt);867                    }868 869                    _progressEvents.Clear();870                }871            }872        }873 874        /// <summary>875        /// Installs HelpInfo.xml.876        /// </summary>877        /// <param name="moduleName"></param>878        /// <param name="moduleGuid"></param>879        /// <param name="culture">Culture updated.</param>880        /// <param name="version">Version updated.</param>881        /// <param name="contentUri">Help content uri.</param>882        /// <param name="destPath">Destination name.</param>883        /// <param name="fileName">Combined file name.</param>884        /// <param name="force">Forces the file to copy.</param>885        internal void GenerateHelpInfo(string moduleName, Guid moduleGuid, string contentUri, string culture, Version version, string destPath, string fileName, bool force)886        {887            Debug.Assert(Directory.Exists(destPath));888 889            if (_stopping)890            {891                return;892            }893 894            string destHelpInfo = Path.Combine(destPath, fileName);895 896            if (force)897            {898                RemoveReadOnly(destHelpInfo);899            }900 901            UpdatableHelpInfo oldHelpInfo = null;902            string xml = UpdatableHelpSystem.LoadStringFromPath(_cmdlet, destHelpInfo, null);903 904            if (xml != null)905            {906                // constructing the helpinfo object from previous update help log xml..907                // no need to resolve the uri's in this case.908                oldHelpInfo = CreateHelpInfo(xml, moduleName, moduleGuid, currentCulture: null, pathOverride: null,909                                             verbose: false, shouldResolveUri: false, ignoreValidationException: force);910            }911 912            using (FileStream file = new FileStream(destHelpInfo, FileMode.Create, FileAccess.Write))913            {914                XmlWriterSettings settings = new XmlWriterSettings();915                settings.Encoding = Encoding.UTF8;916                settings.Indent = true; // Default indentation is two spaces917                using (XmlWriter writer = XmlWriter.Create(file, settings))918                {919                    writer.WriteStartDocument();920                    writer.WriteStartElement("HelpInfo", "http://schemas.microsoft.com/powershell/help/2010/05");921 922                    writer.WriteStartElement("HelpContentURI");923                    writer.WriteValue(contentUri);924                    writer.WriteEndElement();925 926                    writer.WriteStartElement("SupportedUICultures");927 928                    bool found = false;929 930                    if (oldHelpInfo != null)931                    {932                        foreach (CultureSpecificUpdatableHelp oldInfo in oldHelpInfo.UpdatableHelpItems)933                        {934                            if (oldInfo.Culture.Name.Equals(culture, StringComparison.OrdinalIgnoreCase))935                            {936                                if (oldInfo.Version.Equals(version))937                                {938                                    writer.WriteStartElement("UICulture");939                                    writer.WriteStartElement("UICultureName");940                                    writer.WriteValue(oldInfo.Culture.Name);941                                    writer.WriteEndElement();942                                    writer.WriteStartElement("UICultureVersion");943                                    writer.WriteValue(oldInfo.Version.ToString());944                                    writer.WriteEndElement();945                                    writer.WriteEndElement();946                                }947                                else948                                {949                                    writer.WriteStartElement("UICulture");950                                    writer.WriteStartElement("UICultureName");951                                    writer.WriteValue(culture);952                                    writer.WriteEndElement();953                                    writer.WriteStartElement("UICultureVersion");954                                    writer.WriteValue(version.ToString());955                                    writer.WriteEndElement();956                                    writer.WriteEndElement();957                                }958 959                                found = true;960                            }961                            else962                            {963                                writer.WriteStartElement("UICulture");964                                writer.WriteStartElement("UICultureName");965                                writer.WriteValue(oldInfo.Culture.Name);966                                writer.WriteEndElement();967                                writer.WriteStartElement("UICultureVersion");968                                writer.WriteValue(oldInfo.Version.ToString());969                                writer.WriteEndElement();970                                writer.WriteEndElement();971                            }972                        }973                    }974 975                    if (!found)976                    {977                        writer.WriteStartElement("UICulture");978                        writer.WriteStartElement("UICultureName");979                        writer.WriteValue(culture);980                        writer.WriteEndElement();981                        writer.WriteStartElement("UICultureVersion");982                        writer.WriteValue(version.ToString());983                        writer.WriteEndElement();984                        writer.WriteEndElement();985                    }986 987                    writer.WriteEndElement();988                    writer.WriteEndDocument();989                }990            }991        }992 993        /// <summary>994        /// Removes the read only attribute.995        /// </summary>996        /// <param name="path"></param>997        private static void RemoveReadOnly(string path)998        {999            if (File.Exists(path))1000            {1001                FileAttributes attributes = File.GetAttributes(path);1002 1003                if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)1004                {1005                    attributes &= ~FileAttributes.ReadOnly;1006                    File.SetAttributes(path, attributes);1007                }1008            }1009        }1010 1011        /// <summary>1012        /// Installs (unzips) the help content.1013        /// </summary>1014        /// <param name="commandType">Command type.</param>1015        /// <param name="context">Execution context.</param>1016        /// <param name="sourcePath">Source directory.</param>1017        /// <param name="destPaths">Destination paths.</param>1018        /// <param name="fileName">Help content file name.</param>1019        /// <param name="tempPath">Temporary path.</param>1020        /// <param name="culture">Current culture.</param>1021        /// <param name="xsdPath">Path of the maml XSDs.</param>1022        /// <param name="installed">Files installed.</param>1023        /// <remarks>1024        /// Directory pointed by <paramref name="tempPath"/> (if any) will be deleted.1025        /// </remarks>1026        internal void InstallHelpContent(UpdatableHelpCommandType commandType, ExecutionContext context, string sourcePath,1027            Collection<string> destPaths, string fileName, string tempPath, CultureInfo culture, string xsdPath,1028            out Collection<string> installed)1029        {1030            installed = new Collection<string>();1031 1032            if (_stopping)1033            {1034                installed = new Collection<string>();1035                return;1036            }1037 1038            // These paths must exist1039            if (!Directory.Exists(tempPath))1040            {1041                Directory.CreateDirectory(tempPath);1042            }1043 1044            Debug.Assert(destPaths.Count > 0);1045 1046            try1047            {1048                OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format(1049                    HelpDisplayStrings.UpdateProgressInstalling), 0));1050 1051                string combinedSourcePath = GetFilePath(Path.Combine(sourcePath, fileName));1052 1053                if (string.IsNullOrEmpty(combinedSourcePath))1054                {1055                    throw new UpdatableHelpSystemException("HelpContentNotFound", StringUtil.Format(HelpDisplayStrings.HelpContentNotFound),1056                        ErrorCategory.ResourceUnavailable, null, null);1057                }1058 1059                string combinedTempPath = Path.Combine(tempPath, Path.GetFileNameWithoutExtension(fileName));1060 1061                if (Directory.Exists(combinedTempPath))1062                {1063                    Directory.Delete(combinedTempPath, true);1064                }1065 1066                bool needToCopy = true;1067                UnzipHelpContent(context, combinedSourcePath, combinedTempPath, out needToCopy);1068                if (needToCopy)1069                {1070                    ValidateAndCopyHelpContent(combinedTempPath, destPaths, culture.Name, xsdPath, out installed);1071                }1072            }1073            finally1074            {1075                OnProgressChanged(this, new UpdatableHelpProgressEventArgs(CurrentModule, commandType, StringUtil.Format(1076                    HelpDisplayStrings.UpdateProgressInstalling), 100));1077 1078                try1079                {1080                    if (Directory.Exists(tempPath))1081                    {1082                        Directory.Delete(tempPath);1083                    }1084                }1085                catch (IOException) { }1086                catch (UnauthorizedAccessException) { }1087                catch (ArgumentException) { }1088            }1089        }1090 1091#if UNIX1092        private static bool ExpandArchive(string source, string destination)1093        {1094            bool successfulDecompression = false;1095 1096            try1097            {1098                using (ZipArchive zipArchive = ZipFile.Open(source, ZipArchiveMode.Read))1099                {1100                    zipArchive.ExtractToDirectory(destination);1101                    successfulDecompression = true;1102                }1103            }1104            catch (ArgumentException) { }1105            catch (InvalidDataException) { }1106            catch (NotSupportedException) { }1107            catch (FileNotFoundException) { }1108            catch (IOException) { }1109            catch (SecurityException) { }1110            catch (UnauthorizedAccessException) { }1111            catch (ObjectDisposedException) { }1112 1113            return successfulDecompression;1114        }1115#endif1116 1117        /// <summary>1118        /// Unzips to help content to a given location.1119        /// </summary>1120        /// <param name="context">Execution context.</param>1121        /// <param name="srcPath">Source path.</param>1122        /// <param name="destPath">Destination path.</param>1123        /// <param name="needToCopy">Is set to false if we find a single file placeholder.txt in cab. This means we no longer need to install help files.</param>1124        private static void UnzipHelpContent(ExecutionContext context, string srcPath, string destPath, out bool needToCopy)1125        {1126            needToCopy = true;1127 1128            if (!Directory.Exists(destPath))1129            {1130                Directory.CreateDirectory(destPath);1131            }1132 1133            string sourceDirectory = Path.GetDirectoryName(srcPath);1134            bool successfulDecompression = false;1135#if UNIX1136            successfulDecompression = ExpandArchive(Path.Combine(sourceDirectory, Path.GetFileName(srcPath)), destPath);1137#else1138            // Cabinet API doesn't handle the trailing back slash1139            if (!sourceDirectory.EndsWith('\\'))1140            {1141                sourceDirectory += "\\";1142            }1143 1144            if (!destPath.EndsWith('\\'))1145            {1146                destPath += "\\";1147            }1148 1149            successfulDecompression = CabinetExtractorFactory.GetCabinetExtractor().Extract(Path.GetFileName(srcPath), sourceDirectory, destPath);1150#endif1151            if (!successfulDecompression)1152            {1153                throw new UpdatableHelpSystemException("UnableToExtract", StringUtil.Format(HelpDisplayStrings.UnzipFailure),1154                    ErrorCategory.InvalidOperation, null, null);1155            }1156 1157            string[] files = Directory.GetFiles(destPath);1158            if (files.Length == 1)1159            {1160                // If there is a single file1161                string file = Path.GetFileName(files[0]);1162                if (!string.IsNullOrEmpty(file) && file.Equals("placeholder.txt", StringComparison.OrdinalIgnoreCase))1163                {1164                    // And that single file is named "placeholder.txt"1165                    var fileInfo = new FileInfo(files[0]);1166                    if (fileInfo.Length == 0)1167                    {1168                        // And if that single file has length 0, then we delete that file and no longer install help1169                        needToCopy = false;1170                        try1171                        {1172                            File.Delete(files[0]);1173                            string directory = Path.GetDirectoryName(files[0]);1174                            if (!string.IsNullOrEmpty(directory))1175                            {1176                                Directory.Delete(directory);1177                            }1178                        }1179                        catch (FileNotFoundException)1180                        { }1181                        catch (DirectoryNotFoundException)1182                        { }1183                        catch (UnauthorizedAccessException)1184                        { }1185                        catch (System.Security.SecurityException)1186                        { }1187                        catch (ArgumentNullException)1188                        { }1189                        catch (ArgumentException)1190                        { }1191                        catch (PathTooLongException)1192                        { }1193                        catch (NotSupportedException)1194                        { }1195                        catch (IOException)1196                        { }1197                    }1198                }1199            }1200            else

Showing the first 1,200 of 1655 lines. Download the file for the rest.