MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics;8using System.Diagnostics.CodeAnalysis;9using System.Globalization;10using System.IO;11using System.Linq;12using System.Management.Automation;13using System.Management.Automation.Help;14using System.Management.Automation.Internal;15using System.Management.Automation.Runspaces;16using System.Management.Automation.Tracing;17using System.Net;18 19namespace Microsoft.PowerShell.Commands20{21 /// <summary>22 /// The base class of all updatable help system cmdlets (Update-Help, Save-Help)23 /// </summary>24 public class UpdatableHelpCommandBase : PSCmdlet25 {26 internal const string PathParameterSetName = "Path";27 internal const string LiteralPathParameterSetName = "LiteralPath";28 29 internal UpdatableHelpCommandType _commandType;30 internal UpdatableHelpSystem _helpSystem;31 internal bool _stopping;32 33 internal int activityId;34 private readonly Dictionary<string, UpdatableHelpExceptionContext> _exceptions;35 36 #region Parameters37 38 /// <summary>39 /// Specifies the languages to update.40 /// </summary>41 [Parameter(Position = 2)]42 [ValidateNotNull]43 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]44 public CultureInfo[] UICulture45 {46 get47 {48 CultureInfo[] result = null;49 if (_language != null)50 {51 result = new CultureInfo[_language.Length];52 for (int index = 0; index < _language.Length; index++)53 {54 result[index] = new CultureInfo(_language[index]);55 }56 }57 58 return result;59 }60 61 set62 {63 if (value == null)64 {65 return;66 }67 68 _language = new string[value.Length];69 for (int index = 0; index < value.Length; index++)70 {71 _language[index] = value[index].Name;72 }73 }74 }75 76 internal string[] _language;77 78 /// <summary>79 /// Gets or sets the credential parameter.80 /// </summary>81 [Parameter]82 [Credential]83 public PSCredential Credential84 {85 get { return _credential; }86 87 set { _credential = value; }88 }89 90 internal PSCredential _credential;91 92 /// <summary>93 /// Directs System.Net.WebClient whether or not to use default credentials.94 /// </summary>95 [Parameter]96 public SwitchParameter UseDefaultCredentials97 {98 get99 {100 return _useDefaultCredentials;101 }102 103 set104 {105 _useDefaultCredentials = value;106 }107 }108 109 private bool _useDefaultCredentials = false;110 111 /// <summary>112 /// Forces the operation to complete.113 /// </summary>114 [Parameter]115 public SwitchParameter Force116 {117 get118 {119 return _force;120 }121 122 set123 {124 _force = value;125 }126 }127 128 internal bool _force;129 130 /// <summary>131 /// Sets the scope to which help is saved.132 /// </summary>133 [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)]134 public UpdateHelpScope Scope135 {136 get;137 set;138 }139 140 #endregion141 142 #region Events143 144 /// <summary>145 /// Handles help system progress events.146 /// </summary>147 /// <param name="sender">Event sender.</param>148 /// <param name="e">Event arguments.</param>149 private void HandleProgressChanged(object sender, UpdatableHelpProgressEventArgs e)150 {151 Debug.Assert(e.CommandType == UpdatableHelpCommandType.UpdateHelpCommand152 || e.CommandType == UpdatableHelpCommandType.SaveHelpCommand);153 154 string activity = (e.CommandType == UpdatableHelpCommandType.UpdateHelpCommand) ?155 HelpDisplayStrings.UpdateProgressActivityForModule : HelpDisplayStrings.SaveProgressActivityForModule;156 157 ProgressRecord progress = new ProgressRecord(activityId, StringUtil.Format(activity, e.ModuleName), e.ProgressStatus);158 159 progress.PercentComplete = e.ProgressPercent;160 161 WriteProgress(progress);162 }163 164 #endregion165 166 #region Constructor167 168 private static readonly Dictionary<string, string> s_metadataCache;169 170 /// <summary>171 /// Static constructor172 ///173 /// NOTE: HelpInfoUri for core PowerShell modules are needed since they get loaded as snapins in a Remoting Endpoint.174 /// When we moved to modules in V3, we were not able to make this change as it was a risky change to make at that time.175 /// </summary>176 static UpdatableHelpCommandBase()177 {178 s_metadataCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);179 180 // NOTE: The HelpInfoUri must be updated with each release.181 182 s_metadataCache.Add("Microsoft.PowerShell.Diagnostics", "https://aka.ms/powershell75-help");183 s_metadataCache.Add("Microsoft.PowerShell.Core", "https://aka.ms/powershell75-help");184 s_metadataCache.Add("Microsoft.PowerShell.Utility", "https://aka.ms/powershell75-help");185 s_metadataCache.Add("Microsoft.PowerShell.Host", "https://aka.ms/powershell75-help");186 s_metadataCache.Add("Microsoft.PowerShell.Management", "https://aka.ms/powershell75-help");187 s_metadataCache.Add("Microsoft.PowerShell.Security", "https://aka.ms/powershell75-help");188 s_metadataCache.Add("Microsoft.WSMan.Management", "https://aka.ms/powershell75-help");189 }190 191 /// <summary>192 /// Checks if a module is a system module, a module is a system module193 /// if it exists in the metadata cache.194 /// </summary>195 /// <param name="module">Module name.</param>196 /// <returns>True if system module, false if not.</returns>197 internal static bool IsSystemModule(string module)198 {199 return s_metadataCache.ContainsKey(module);200 }201 202 /// <summary>203 /// Class constructor.204 /// </summary>205 /// <param name="commandType">Command type.</param>206 internal UpdatableHelpCommandBase(UpdatableHelpCommandType commandType)207 {208 _commandType = commandType;209 _helpSystem = new UpdatableHelpSystem(this, _useDefaultCredentials);210 _exceptions = new Dictionary<string, UpdatableHelpExceptionContext>();211 _helpSystem.OnProgressChanged += HandleProgressChanged;212 213 activityId = Random.Shared.Next();214 }215 216 #endregion217 218 #region Implementation219 220 private void ProcessSingleModuleObject(PSModuleInfo module, ExecutionContext context, Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo> helpModules, bool noErrors)221 {222 if (InitialSessionState.IsEngineModule(module.Name) && !InitialSessionState.IsNestedEngineModule(module.Name))223 {224 WriteDebug(StringUtil.Format("Found engine module: {0}, {1}.", module.Name, module.Guid));225 226 var keyTuple = new Tuple<string, Version>(module.Name, module.Version);227 if (!helpModules.ContainsKey(keyTuple))228 {229 helpModules.Add(keyTuple, new UpdatableHelpModuleInfo(module.Name, module.Guid,230 Utils.GetApplicationBase(context.ShellID), s_metadataCache[module.Name]));231 }232 233 return;234 }235 else if (InitialSessionState.IsNestedEngineModule(module.Name))236 {237 return;238 }239 240 if (string.IsNullOrEmpty(module.HelpInfoUri))241 {242 if (!noErrors)243 {244 ProcessException(module.Name, null, new UpdatableHelpSystemException(245 "HelpInfoUriNotFound", StringUtil.Format(HelpDisplayStrings.HelpInfoUriNotFound),246 ErrorCategory.NotSpecified, new Uri("HelpInfoUri", UriKind.Relative), null));247 }248 249 return;250 }251 252 if (!(module.HelpInfoUri.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || module.HelpInfoUri.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))253 {254 if (!noErrors)255 {256 ProcessException(module.Name, null, new UpdatableHelpSystemException(257 "InvalidHelpInfoUriFormat", StringUtil.Format(HelpDisplayStrings.InvalidHelpInfoUriFormat, module.HelpInfoUri),258 ErrorCategory.NotSpecified, new Uri("HelpInfoUri", UriKind.Relative), null));259 }260 261 return;262 }263 264 var keyTuple2 = new Tuple<string, Version>(module.Name, module.Version);265 if (!helpModules.ContainsKey(keyTuple2))266 {267 helpModules.Add(keyTuple2, new UpdatableHelpModuleInfo(module.Name, module.Guid, module.ModuleBase, module.HelpInfoUri));268 }269 }270 271 /// <summary>272 /// Gets a list of modules from the given pattern.273 /// </summary>274 /// <param name="context">Execution context.</param>275 /// <param name="pattern">Pattern to search.</param>276 /// <param name="fullyQualifiedName">Module Specification.</param>277 /// <param name="noErrors">Do not generate errors for modules without HelpInfoUri.</param>278 /// <returns>A list of modules.</returns>279 private Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo> GetModuleInfo(ExecutionContext context, string pattern, ModuleSpecification fullyQualifiedName, bool noErrors)280 {281 List<PSModuleInfo> modules = null;282 string moduleNamePattern = null;283 284 if (pattern != null)285 {286 moduleNamePattern = pattern;287 modules = Utils.GetModules(pattern, context);288 }289 else if (fullyQualifiedName != null)290 {291 moduleNamePattern = fullyQualifiedName.Name;292 modules = Utils.GetModules(fullyQualifiedName, context);293 }294 295 var helpModules = new Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo>();296 if (modules != null)297 {298 foreach (PSModuleInfo module in modules)299 {300 ProcessSingleModuleObject(module, context, helpModules, noErrors);301 }302 }303 304 IEnumerable<WildcardPattern> patternList = SessionStateUtilities.CreateWildcardsFromStrings(305 globPatterns: new[] { moduleNamePattern },306 options: WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant);307 308 foreach (KeyValuePair<string, string> name in s_metadataCache)309 {310 if (SessionStateUtilities.MatchesAnyWildcardPattern(name.Key, patternList, true))311 {312 // For core snapin, there are no GUIDs. So, we need to construct the HelpInfo slightly differently313 if (!name.Key.Equals(InitialSessionState.CoreSnapin, StringComparison.OrdinalIgnoreCase))314 {315 var keyTuple = new Tuple<string, Version>(name.Key, new Version("1.0"));316 if (!helpModules.ContainsKey(keyTuple))317 {318 List<PSModuleInfo> availableModules = Utils.GetModules(name.Key, context);319 if (availableModules != null)320 {321 foreach (PSModuleInfo module in availableModules)322 {323 keyTuple = new Tuple<string, Version>(module.Name, module.Version);324 if (!helpModules.ContainsKey(keyTuple))325 {326 WriteDebug(StringUtil.Format("Found engine module: {0}, {1}.", module.Name, module.Guid));327 328 helpModules.Add(keyTuple, new UpdatableHelpModuleInfo(module.Name,329 module.Guid, Utils.GetApplicationBase(context.ShellID), s_metadataCache[module.Name]));330 }331 }332 }333 }334 }335 else336 {337 var keyTuple2 = new Tuple<string, Version>(name.Key, new Version("1.0"));338 if (!helpModules.ContainsKey(keyTuple2))339 {340 helpModules.Add(keyTuple2,341 new UpdatableHelpModuleInfo(name.Key, Guid.Empty,342 Utils.GetApplicationBase(context.ShellID),343 name.Value));344 }345 }346 }347 }348 349 return helpModules;350 }351 352 /// <summary>353 /// Handles Ctrl+C.354 /// </summary>355 protected override void StopProcessing()356 {357 _stopping = true;358 _helpSystem.CancelDownload();359 }360 361 /// <summary>362 /// End processing.363 /// </summary>364 protected override void EndProcessing()365 {366 foreach (UpdatableHelpExceptionContext exception in _exceptions.Values)367 {368 UpdatableHelpExceptionContext e = exception;369 370 if ((exception.Exception.FullyQualifiedErrorId == "HelpCultureNotSupported") &&371 ((exception.Cultures != null && exception.Cultures.Count > 1) ||372 (exception.Modules != null && exception.Modules.Count > 1)))373 {374 // Win8: 744749 Rewriting the error message only in the case where either375 // multiple cultures or multiple modules are involved.376 e = new UpdatableHelpExceptionContext(new UpdatableHelpSystemException(377 "HelpCultureNotSupported", StringUtil.Format(HelpDisplayStrings.CannotMatchUICulturePattern,378 string.Join(", ", exception.Cultures)),379 ErrorCategory.InvalidArgument, exception.Cultures, null));380 e.Modules = exception.Modules;381 e.Cultures = exception.Cultures;382 }383 384 WriteError(e.CreateErrorRecord(_commandType));385 386 LogContext context = MshLog.GetLogContext(Context, MyInvocation);387 388 context.Severity = "Error";389 390 PSEtwLog.LogOperationalError(PSEventId.Pipeline_Detail, PSOpcode.Exception, PSTask.ExecutePipeline,391 context, e.GetExceptionMessage(_commandType));392 }393 }394 395 /// <summary>396 /// Main cmdlet logic for processing module names or fully qualified module names.397 /// </summary>398 /// <param name="moduleNames">Module names given by the user.</param>399 /// <param name="fullyQualifiedNames">FullyQualifiedNames.</param>400 internal void Process(IEnumerable<string> moduleNames, IEnumerable<ModuleSpecification> fullyQualifiedNames)401 {402 _helpSystem.UseDefaultCredentials = _useDefaultCredentials;403 404 if (moduleNames != null)405 {406 foreach (string name in moduleNames)407 {408 if (_stopping)409 {410 break;411 }412 413 ProcessModuleWithGlobbing(name);414 }415 }416 else if (fullyQualifiedNames != null)417 {418 foreach (var fullyQualifiedName in fullyQualifiedNames)419 {420 if (_stopping)421 {422 break;423 }424 425 ProcessModuleWithGlobbing(fullyQualifiedName);426 }427 }428 else429 {430 foreach (KeyValuePair<Tuple<string, Version>, UpdatableHelpModuleInfo> module in GetModuleInfo("*", null, true))431 {432 if (_stopping)433 {434 break;435 }436 437 ProcessModule(module.Value);438 }439 }440 }441 442 /// <summary>443 /// Processing module objects for Save-Help.444 /// </summary>445 /// <param name="modules">Module objects given by the user.</param>446 internal void Process(IEnumerable<PSModuleInfo> modules)447 {448 if (modules == null || !modules.Any())449 {450 return;451 }452 453 var helpModules = new Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo>();454 455 foreach (PSModuleInfo module in modules)456 {457 ProcessSingleModuleObject(module, Context, helpModules, false);458 }459 460 foreach (KeyValuePair<Tuple<string, Version>, UpdatableHelpModuleInfo> helpModule in helpModules)461 {462 ProcessModule(helpModule.Value);463 }464 }465 466 /// <summary>467 /// Processes a module with potential globbing.468 /// </summary>469 /// <param name="name">Module name with globbing.</param>470 private void ProcessModuleWithGlobbing(string name)471 {472 if (string.IsNullOrEmpty(name))473 {474 PSArgumentException e = new PSArgumentException(StringUtil.Format(HelpDisplayStrings.ModuleNameNullOrEmpty));475 WriteError(e.ErrorRecord);476 return;477 }478 479 foreach (KeyValuePair<Tuple<string, Version>, UpdatableHelpModuleInfo> module in GetModuleInfo(name, null, false))480 {481 ProcessModule(module.Value);482 }483 }484 485 /// <summary>486 /// Processes a ModuleSpecification with potential globbing.487 /// </summary>488 /// <param name="fullyQualifiedName">ModuleSpecification.</param>489 private void ProcessModuleWithGlobbing(ModuleSpecification fullyQualifiedName)490 {491 foreach (KeyValuePair<Tuple<string, Version>, UpdatableHelpModuleInfo> module in GetModuleInfo(null, fullyQualifiedName, false))492 {493 ProcessModule(module.Value);494 }495 }496 497 /// <summary>498 /// Processes a single module with multiple cultures.499 /// </summary>500 /// <param name="module">Module to process.</param>501 private void ProcessModule(UpdatableHelpModuleInfo module)502 {503 _helpSystem.CurrentModule = module.ModuleName;504 505 if (this is UpdateHelpCommand && !Directory.Exists(module.ModuleBase))506 {507 ProcessException(module.ModuleName, null,508 new UpdatableHelpSystemException("ModuleBaseMustExist",509 StringUtil.Format(HelpDisplayStrings.ModuleBaseMustExist),510 ErrorCategory.InvalidOperation, null, null));511 return;512 }513 514 // Win8: 572882 When the system locale is English and the UI is JPN,515 // running "update-help" still downs English help content.516 var cultures = _language ?? _helpSystem.GetCurrentUICulture();517 UpdatableHelpSystemException implicitCultureNotSupported = null;518 519 foreach (string culture in cultures)520 {521 bool installed = true;522 523 if (_stopping)524 {525 break;526 }527 528 try529 {530 ProcessModuleWithCulture(module, culture);531 }532 catch (IOException e)533 {534 ProcessException(module.ModuleName, culture, new UpdatableHelpSystemException("FailedToCopyFile",535 e.Message, ErrorCategory.InvalidOperation, null, e));536 }537 catch (UnauthorizedAccessException e)538 {539 ProcessException(module.ModuleName, culture, new UpdatableHelpSystemException("AccessIsDenied",540 e.Message, ErrorCategory.PermissionDenied, null, e));541 }542#if !CORECLR543 catch (WebException e)544 {545 if (e.InnerException != null && e.InnerException is UnauthorizedAccessException)546 {547 ProcessException(module.ModuleName, culture, new UpdatableHelpSystemException("AccessIsDenied",548 e.InnerException.Message, ErrorCategory.PermissionDenied, null, e));549 }550 else551 {552 ProcessException(module.ModuleName, culture, e);553 }554 }555#endif556 catch (UpdatableHelpSystemException e)557 {558 if (e.FullyQualifiedErrorId == "HelpCultureNotSupported"559 || e.FullyQualifiedErrorId == "UnableToRetrieveHelpInfoXml")560 {561 installed = false;562 563 if (_language != null)564 {565 // Display the error message only if we are not using the fallback chain566 ProcessException(module.ModuleName, culture, e);567 }568 else569 {570 // Hold first exception, it will be displayed if fallback chain fails571 WriteVerbose(StringUtil.Format(HelpDisplayStrings.HelpCultureNotSupportedFallback, e.Message));572 implicitCultureNotSupported ??= e;573 }574 }575 else576 {577 ProcessException(module.ModuleName, culture, e);578 }579 }580 catch (Exception e)581 {582 ProcessException(module.ModuleName, culture, e);583 }584 finally585 {586 if (_helpSystem.Errors.Count != 0)587 {588 foreach (Exception error in _helpSystem.Errors)589 {590 ProcessException(module.ModuleName, culture, error);591 }592 593 _helpSystem.Errors.Clear();594 }595 }596 597 // If -UICulture is not specified, we only install598 // one culture from the fallback chain599 if (_language == null && installed)600 {601 return;602 }603 }604 605 // If the exception is not null and did not return early, then all of the fallback chain failed606 if (implicitCultureNotSupported != null)607 {608 ProcessException(module.ModuleName, cultures.First(), implicitCultureNotSupported);609 }610 }611 612 /// <summary>613 /// Process a single module with a given culture.614 /// </summary>615 /// <param name="module">Module to process.</param>616 /// <param name="culture">Culture to use.</param>617 /// <returns>True if the module has been processed, false if not.</returns>618 internal virtual bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, string culture)619 {620 return false;621 }622 623 #endregion624 625 #region Common methods626 627 /// <summary>628 /// Gets a list of modules from the given pattern or ModuleSpecification.629 /// </summary>630 /// <param name="pattern">Pattern to match.</param>631 /// <param name="fullyQualifiedName">ModuleSpecification.</param>632 /// <param name="noErrors">Skip errors.</param>633 /// <returns>A list of modules.</returns>634 internal Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo> GetModuleInfo(string pattern, ModuleSpecification fullyQualifiedName, bool noErrors)635 {636 Dictionary<Tuple<string, Version>, UpdatableHelpModuleInfo> modules = GetModuleInfo(Context, pattern, fullyQualifiedName, noErrors);637 638 if (modules.Count == 0 && _exceptions.Count == 0 && !noErrors)639 {640 var errorMessage = fullyQualifiedName != null ? StringUtil.Format(HelpDisplayStrings.ModuleNotFoundWithFullyQualifiedName, fullyQualifiedName)641 : StringUtil.Format(HelpDisplayStrings.CannotMatchModulePattern, pattern);642 643 ErrorRecord errorRecord = new ErrorRecord(new Exception(errorMessage),644 "ModuleNotFound", ErrorCategory.InvalidArgument, pattern);645 646 WriteError(errorRecord);647 }648 649 return modules;650 }651 652 /// <summary>653 /// Checks if it is necessary to update help.654 /// </summary>655 /// <param name="module">ModuleInfo.</param>656 /// <param name="currentHelpInfo">Current HelpInfo.xml.</param>657 /// <param name="newHelpInfo">New HelpInfo.xml.</param>658 /// <param name="culture">Current culture.</param>659 /// <param name="force">Force update.</param>660 /// <returns>True if it is necessary to update help, false if not.</returns>661 internal bool IsUpdateNecessary(UpdatableHelpModuleInfo module, UpdatableHelpInfo currentHelpInfo,662 UpdatableHelpInfo newHelpInfo, CultureInfo culture, bool force)663 {664 Debug.Assert(module != null);665 666 if (newHelpInfo == null)667 {668 throw new UpdatableHelpSystemException("UnableToRetrieveHelpInfoXml",669 StringUtil.Format(HelpDisplayStrings.UnableToRetrieveHelpInfoXml, culture.Name), ErrorCategory.ResourceUnavailable,670 null, null);671 }672 673 // Culture check674 if (!newHelpInfo.IsCultureSupported(culture.Name))675 {676 throw new UpdatableHelpSystemException("HelpCultureNotSupported",677 StringUtil.Format(HelpDisplayStrings.HelpCultureNotSupported,678 culture.Name, newHelpInfo.GetSupportedCultures()), ErrorCategory.InvalidOperation, null, null);679 }680 681 // Version check682 if (!force && currentHelpInfo != null && !currentHelpInfo.IsNewerVersion(newHelpInfo, culture))683 {684 return false;685 }686 687 return true;688 }689 690 /// <summary>691 /// Checks if the user has attempted to update more than once per day per module.692 /// </summary>693 /// <param name="moduleName">Module name.</param>694 /// <param name="path">Path to help info.</param>695 /// <param name="filename">Help info file name.</param>696 /// <param name="time">Current time (UTC).</param>697 /// <param name="force">If -Force is specified.</param>698 /// <returns>True if we are okay to update, false if not.</returns>699 internal bool CheckOncePerDayPerModule(string moduleName, string path, string filename, DateTime time, bool force)700 {701 // Update if -Force is specified702 if (force)703 {704 return true;705 }706 707 string helpInfoFilePath = SessionState.Path.Combine(path, filename);708 709 // No HelpInfo.xml710 if (!File.Exists(helpInfoFilePath))711 {712 return true;713 }714 715 DateTime lastModified = File.GetLastWriteTimeUtc(helpInfoFilePath);716 TimeSpan difference = time - lastModified;717 718 if (difference.Days >= 1)719 {720 return true;721 }722 723 if (_commandType == UpdatableHelpCommandType.UpdateHelpCommand)724 {725 WriteVerbose(StringUtil.Format(HelpDisplayStrings.UseForceToUpdateHelp, moduleName));726 }727 else if (_commandType == UpdatableHelpCommandType.SaveHelpCommand)728 {729 WriteVerbose(StringUtil.Format(HelpDisplayStrings.UseForceToSaveHelp, moduleName));730 }731 732 return false;733 }734 735 /// <summary>736 /// Resolves a given path to a list of directories.737 /// </summary>738 /// <param name="path">Path to resolve.</param>739 /// <param name="recurse">Resolve recursively?</param>740 /// <param name="isLiteralPath">Treat the path / start path as a literal path?</param>///741 /// <returns>A list of directories.</returns>742 internal IEnumerable<string> ResolvePath(string path, bool recurse, bool isLiteralPath)743 {744 List<string> resolvedPaths = new List<string>();745 746 if (isLiteralPath)747 {748 string newPath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(path);749 750 if (!Directory.Exists(newPath))751 {752 throw new UpdatableHelpSystemException("PathMustBeValidContainers",753 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument,754 null, new ItemNotFoundException());755 }756 757 resolvedPaths.Add(newPath);758 }759 else760 {761 Collection<PathInfo> resolvedPathInfos = SessionState.Path.GetResolvedPSPathFromPSPath(path);762 foreach (PathInfo resolvedPath in resolvedPathInfos)763 {764 ValidatePathProvider(resolvedPath);765 766 resolvedPaths.Add(resolvedPath.ProviderPath);767 }768 }769 770 foreach (string resolvedPath in resolvedPaths)771 {772 if (recurse)773 {774 foreach (string innerResolvedPath in RecursiveResolvePathHelper(resolvedPath))775 {776 yield return innerResolvedPath;777 }778 }779 else780 {781 // Win8: 566738782 CmdletProviderContext context = new CmdletProviderContext(this.Context);783 // resolvedPath is already resolved..so no need to expand wildcards anymore784 context.SuppressWildcardExpansion = true;785 if (isLiteralPath || InvokeProvider.Item.IsContainer(resolvedPath, context))786 {787 yield return resolvedPath;788 }789 }790 }791 792 yield break;793 }794 795 /// <summary>796 /// Resolves a given path to a list of directories recursively.797 /// </summary>798 /// <param name="path">Path to resolve.</param>799 /// <returns>A list of directories.</returns>800 private static IEnumerable<string> RecursiveResolvePathHelper(string path)801 {802 if (System.IO.Directory.Exists(path))803 {804 yield return path;805 806 foreach (string subDirectory in Directory.EnumerateDirectories(path))807 {808 foreach (string subDirectory2 in RecursiveResolvePathHelper(subDirectory))809 {810 yield return subDirectory2;811 }812 }813 }814 815 yield break;816 }817 818 #endregion819 820 #region Static methods821 822 /// <summary>823 /// Validates the provider of the path, only FileSystem provider is accepted.824 /// </summary>825 /// <param name="path">Path to validate.</param>826 internal void ValidatePathProvider(PathInfo path)827 {828 if (path.Provider == null || path.Provider.Name != FileSystemProvider.ProviderName)829 {830 throw new PSArgumentException(StringUtil.Format(HelpDisplayStrings.ProviderIsNotFileSystem,831 path.Path));832 }833 }834 835 #endregion836 837 #region Logging838 839 /// <summary>840 /// Logs a command message.841 /// </summary>842 /// <param name="message">Message to log.</param>843 internal void LogMessage(string message)844 {845 List<string> details = new List<string>() { message };846 PSEtwLog.LogPipelineExecutionDetailEvent(MshLog.GetLogContext(Context, Context.CurrentCommandProcessor.Command.MyInvocation), details);847 }848 849 #endregion850 851 #region Exception processing852 853 /// <summary>854 /// Processes an exception for help cmdlets.855 /// </summary>856 /// <param name="moduleName">Module name.</param>857 /// <param name="culture">Culture info.</param>858 /// <param name="e">Exception to check.</param>859 internal void ProcessException(string moduleName, string culture, Exception e)860 {861 UpdatableHelpSystemException except = null;862 863 if (e is UpdatableHelpSystemException)864 {865 except = (UpdatableHelpSystemException)e;866 }867#if !CORECLR868 else if (e is WebException)869 {870 except = new UpdatableHelpSystemException("UnableToConnect",871 StringUtil.Format(HelpDisplayStrings.UnableToConnect), ErrorCategory.InvalidOperation, null, e);872 }873#endif874 else if (e is PSArgumentException)875 {876 except = new UpdatableHelpSystemException("InvalidArgument",877 e.Message, ErrorCategory.InvalidArgument, null, e);878 }879 else880 {881 except = new UpdatableHelpSystemException("UnknownErrorId",882 e.Message, ErrorCategory.InvalidOperation, null, e);883 }884 885 if (!_exceptions.ContainsKey(except.FullyQualifiedErrorId))886 {887 _exceptions.Add(except.FullyQualifiedErrorId, new UpdatableHelpExceptionContext(except));888 }889 890 _exceptions[except.FullyQualifiedErrorId].Modules.Add(moduleName);891 892 if (culture != null)893 {894 _exceptions[except.FullyQualifiedErrorId].Cultures.Add(culture);895 }896 }897 898 #endregion899 }900 901 /// <summary>902 /// Scope to which the help should be saved.903 /// </summary>904 public enum UpdateHelpScope905 {906 /// <summary>907 /// Save the help content to the user directory.908 /// </summary>909 CurrentUser,910 911 /// <summary>912 /// Save the help content to the module directory. This is the default behavior.913 /// </summary>914 AllUsers915 }916}917 