MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.ObjectModel;6using System.Diagnostics;7using System.Diagnostics.CodeAnalysis;8using System.Globalization;9using System.IO;10using System.Linq;11using System.Management.Automation;12using System.Management.Automation.Help;13using System.Management.Automation.Internal;14 15namespace Microsoft.PowerShell.Commands16{17 /// <summary>18 /// This class implements the Update-Help cmdlet.19 /// </summary>20 [Cmdlet(VerbsData.Update, "Help", DefaultParameterSetName = PathParameterSetName,21 SupportsShouldProcess = true,22 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096805")]23 public sealed class UpdateHelpCommand : UpdatableHelpCommandBase24 {25 #region Constructor26 27 /// <summary>28 /// Class constructor.29 /// </summary>30 public UpdateHelpCommand() : base(UpdatableHelpCommandType.UpdateHelpCommand)31 {32 }33 34 #endregion35 36 private bool _alreadyCheckedOncePerDayPerModule = false;37 38 #region Parameters39 40 /// <summary>41 /// Specifies the modules to update.42 /// </summary>43 [Parameter(Position = 0, ParameterSetName = PathParameterSetName, ValueFromPipelineByPropertyName = true)]44 [Parameter(Position = 0, ParameterSetName = LiteralPathParameterSetName, ValueFromPipelineByPropertyName = true)]45 [Alias("Name")]46 [ValidateNotNull]47 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]48 public string[] Module49 {50 get51 {52 return _module;53 }54 55 set56 {57 _module = value;58 }59 }60 61 private string[] _module;62 63 /// <summary>64 /// Specifies the Module Specifications to update.65 /// </summary>66 [Parameter(ParameterSetName = PathParameterSetName, ValueFromPipelineByPropertyName = true)]67 [Parameter(ParameterSetName = LiteralPathParameterSetName, ValueFromPipelineByPropertyName = true)]68 [ValidateNotNull]69 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]70 public ModuleSpecification[] FullyQualifiedModule { get; set; }71 72 /// <summary>73 /// Specifies the paths to update from.74 /// </summary>75 [Parameter(Position = 1, ParameterSetName = PathParameterSetName)]76 [ValidateNotNull]77 [Alias("Path")]78 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]79 public string[] SourcePath80 {81 get82 {83 return _path;84 }85 86 set87 {88 _path = value;89 }90 }91 92 private string[] _path;93 94 /// <summary>95 /// Specifies the literal path to save updates to.96 /// </summary>97 [Parameter(ParameterSetName = LiteralPathParameterSetName, ValueFromPipelineByPropertyName = true)]98 [Alias("PSPath", "LP")]99 [ValidateNotNull]100 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]101 public string[] LiteralPath102 {103 get104 {105 return _path;106 }107 108 set109 {110 _path = value;111 _isLiteralPath = true;112 }113 }114 115 private bool _isLiteralPath = false;116 117 /// <summary>118 /// Scans paths recursively.119 /// </summary>120 [Parameter]121 public SwitchParameter Recurse122 {123 get124 {125 return _recurse;126 }127 128 set129 {130 _recurse = value;131 }132 }133 134 private bool _recurse;135 136 #endregion137 138 private bool _isInitialized = false;139 140 #region Implementation141 142 /// <summary>143 /// Begin processing.144 /// </summary>145 protected override void BeginProcessing()146 {147 if (_path == null)148 {149 // Pull default source path from GP150 string defaultSourcePath = _helpSystem.GetDefaultSourcePath();151 152 if (defaultSourcePath != null)153 {154 _path = new string[1] { defaultSourcePath };155 }156 }157 }158 159 /// <summary>160 /// Main cmdlet logic.161 /// </summary>162 protected override void ProcessRecord()163 {164 try165 {166 // Module and FullyQualifiedModule should not be specified at the same time.167 // Throw out terminating error if this is the case.168 if (Module != null && FullyQualifiedModule != null)169 {170 string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "Module", "FullyQualifiedModule");171 ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null);172 ThrowTerminatingError(error);173 }174 175 if (!_isInitialized)176 {177 if (_path == null && Recurse.IsPresent)178 {179 PSArgumentException e = new PSArgumentException(StringUtil.Format(HelpDisplayStrings.CannotSpecifyRecurseWithoutPath));180 ThrowTerminatingError(e.ErrorRecord);181 }182 183 _isInitialized = true;184 }185 186 // check if there is an UI, if not Throw out terminating error. 187 var cultures = _language ?? _helpSystem.GetCurrentUICulture();188 if (!cultures.Any()) 189 { 190 string cultureString = string.IsNullOrEmpty(CultureInfo.CurrentCulture.Name) ? CultureInfo.CurrentCulture.DisplayName : CultureInfo.CurrentCulture.Name;191 string errMsg = StringUtil.Format(HelpDisplayStrings.FailedToUpdateHelpWithLocaleNoUICulture, cultureString);192 ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailedToUpdateHelpWithLocaleNoUICulture", ErrorCategory.InvalidOperation, targetObject: null); 193 ThrowTerminatingError(error);194 }195 196 base.Process(_module, FullyQualifiedModule);197 198 // Reset the per-runspace help cache199 foreach (HelpProvider provider in Context.HelpSystem.HelpProviders)200 {201 if (_stopping)202 {203 break;204 }205 206 provider.Reset();207 }208 }209 finally210 {211 ProgressRecord progress = new ProgressRecord(activityId, HelpDisplayStrings.UpdateProgressActivityForModule, HelpDisplayStrings.UpdateProgressInstalling);212 213 progress.PercentComplete = 100;214 progress.RecordType = ProgressRecordType.Completed;215 216 WriteProgress(progress);217 }218 }219 220 /// <summary>221 /// Process a single module with a given culture.222 /// </summary>223 /// <param name="module">Module to process.</param>224 /// <param name="culture">Culture to use.</param>225 /// <returns>True if the module has been processed, false if not.</returns>226 internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, string culture)227 {228 // Simulate culture not found229 if (InternalTestHooks.ThrowHelpCultureNotSupported)230 {231 throw new UpdatableHelpSystemException("HelpCultureNotSupported",232 StringUtil.Format(HelpDisplayStrings.HelpCultureNotSupported, culture, "en-US"),233 ErrorCategory.InvalidOperation, null, null);234 }235 236 UpdatableHelpInfo currentHelpInfo = null;237 UpdatableHelpInfo newHelpInfo = null;238 string helpInfoUri = null;239 240 string moduleBase = module.ModuleBase;241 242 if (this.Scope == UpdateHelpScope.CurrentUser)243 {244 moduleBase = HelpUtils.GetModuleBaseForUserHelp(moduleBase, module.ModuleName);245 }246 247 // reading the xml file even if force is specified248 // Reason: we need the current version for ShouldProcess249 string xml = UpdatableHelpSystem.LoadStringFromPath(this,250 SessionState.Path.Combine(moduleBase, module.GetHelpInfoName()),251 null);252 253 if (xml != null)254 {255 // constructing the helpinfo object from previous update help log xml..256 // no need to resolve the uri's in this case.257 currentHelpInfo = _helpSystem.CreateHelpInfo(xml, module.ModuleName, module.ModuleGuid,258 currentCulture: null, pathOverride: null, verbose: false,259 shouldResolveUri: false,260 // ignore validation exception if _force is true261 ignoreValidationException: _force);262 }263 264 // Don't update too frequently265 if (!_alreadyCheckedOncePerDayPerModule && !CheckOncePerDayPerModule(module.ModuleName, moduleBase, module.GetHelpInfoName(), DateTime.UtcNow, _force))266 {267 return true;268 }269 270 _alreadyCheckedOncePerDayPerModule = true;271 272 if (_path != null)273 {274 UpdatableHelpSystemDrive helpInfoDrive = null;275 try276 {277 Collection<string> resolvedPaths = new Collection<string>();278 279 // Search for the HelpInfo XML280 foreach (string path in _path)281 {282 if (string.IsNullOrEmpty(path))283 {284 PSArgumentException e = new PSArgumentException(StringUtil.Format(HelpDisplayStrings.PathNullOrEmpty));285 WriteError(e.ErrorRecord);286 return false;287 }288 289 try290 {291 string sourcePath = path;292 293 if (_credential != null)294 {295 UpdatableHelpSystemDrive drive = new UpdatableHelpSystemDrive(this, path, _credential);296 sourcePath = drive.DriveName;297 }298 299 // Expand wildcard characters300 foreach (string tempPath in ResolvePath(sourcePath, _recurse, _isLiteralPath))301 {302 resolvedPaths.Add(tempPath);303 }304 }305 catch (System.Management.Automation.DriveNotFoundException e)306 {307 ThrowPathMustBeValidContainersException(path, e);308 }309 catch (ItemNotFoundException e)310 {311 ThrowPathMustBeValidContainersException(path, e);312 }313 }314 315 if (resolvedPaths.Count == 0)316 {317 return true;318 }319 320 // Everything in resolvedPaths is a container321 foreach (string resolvedPath in resolvedPaths)322 {323 string literalPath = SessionState.Path.Combine(resolvedPath, module.GetHelpInfoName());324 325 xml = UpdatableHelpSystem.LoadStringFromPath(this, literalPath, _credential);326 327 if (xml != null)328 {329 newHelpInfo = _helpSystem.CreateHelpInfo(xml, module.ModuleName, module.ModuleGuid, culture, resolvedPath,330 verbose: false, shouldResolveUri: true, ignoreValidationException: false);331 helpInfoUri = resolvedPath;332 break;333 }334 }335 }336 catch (Exception e)337 {338 throw new UpdatableHelpSystemException("UnableToRetrieveHelpInfoXml",339 StringUtil.Format(HelpDisplayStrings.UnableToRetrieveHelpInfoXml, culture), ErrorCategory.ResourceUnavailable,340 null, e);341 }342 finally343 {344 helpInfoDrive?.Dispose();345 }346 }347 else348 {349 // Form the actual HelpInfo.xml uri350 helpInfoUri = _helpSystem.GetHelpInfoUri(module, null).ResolvedUri;351 string uri = helpInfoUri + module.GetHelpInfoName();352 353 newHelpInfo = _helpSystem.GetHelpInfo(UpdatableHelpCommandType.UpdateHelpCommand, uri, module.ModuleName, module.ModuleGuid, culture);354 }355 356 if (newHelpInfo == null)357 {358 throw new UpdatableHelpSystemException("UnableToRetrieveHelpInfoXml",359 StringUtil.Format(HelpDisplayStrings.UnableToRetrieveHelpInfoXml, culture), ErrorCategory.ResourceUnavailable,360 null, null);361 }362 363 bool installed = false;364 365 foreach (UpdatableHelpUri contentUri in newHelpInfo.HelpContentUriCollection)366 {367 Version currentHelpVersion = currentHelpInfo?.GetCultureVersion(contentUri.Culture);368 string updateHelpShouldProcessAction = string.Format(CultureInfo.InvariantCulture,369 HelpDisplayStrings.UpdateHelpShouldProcessActionMessage,370 module.ModuleName,371 (currentHelpVersion != null) ? currentHelpVersion.ToString() : "0.0.0.0",372 newHelpInfo.GetCultureVersion(contentUri.Culture),373 contentUri.Culture);374 if (!this.ShouldProcess(updateHelpShouldProcessAction, "Update-Help"))375 {376 continue;377 }378 379 if (Utils.IsUnderProductFolder(moduleBase) && (!Utils.IsAdministrator()))380 {381 string message = StringUtil.Format(HelpErrors.UpdatableHelpRequiresElevation);382 ProcessException(module.ModuleName, null, new UpdatableHelpSystemException("UpdatableHelpSystemRequiresElevation",383 message, ErrorCategory.InvalidOperation, null, null));384 return false;385 }386 387 if (!IsUpdateNecessary(module, _force ? null : currentHelpInfo, newHelpInfo, contentUri.Culture, _force))388 {389 WriteVerbose(StringUtil.Format(HelpDisplayStrings.SuccessfullyUpdatedHelpContent, module.ModuleName, HelpDisplayStrings.NewestContentAlreadyInstalled,390 contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture)));391 392 installed = true;393 continue;394 }395 else396 {397 try398 {399 Debug.Assert(helpInfoUri != null, "If we are here, helpInfoUri must not be null");400 401 string helpContentUri = contentUri.ResolvedUri;402 string xsdPath = SessionState.Path.Combine(Utils.GetApplicationBase(Context.ShellID), "Schemas\\PSMaml\\maml.xsd"); // TODO: Edit the maml XSDs and change this403 404 // Gather destination paths405 Collection<string> destPaths = new Collection<string>();406 407 if (!Directory.Exists(moduleBase))408 {409 Directory.CreateDirectory(moduleBase);410 }411 412 destPaths.Add(moduleBase);413 414#if !CORECLR // Side-By-Side directories are not present in OneCore environments.415 if (IsSystemModule(module.ModuleName) && Environment.Is64BitOperatingSystem)416 {417 string path = Utils.DefaultPowerShellAppBase.Replace("System32", "SysWOW64");418 419 destPaths.Add(path);420 }421#endif422 423 Collection<string> filesInstalled;424 425 if (Directory.Exists(helpContentUri))426 {427 if (_credential != null)428 {429 string helpContentName = module.GetHelpContentName(contentUri.Culture);430 string tempContentPath = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));431 432 try433 {434 using (UpdatableHelpSystemDrive drive = new UpdatableHelpSystemDrive(this, helpContentUri, _credential))435 {436 if (!Directory.Exists(tempContentPath))437 {438 Directory.CreateDirectory(tempContentPath);439 }440 441 InvokeProvider.Item.Copy(new string[1] { Path.Combine(drive.DriveName, helpContentName) },442 Path.Combine(tempContentPath, helpContentName), false, CopyContainers.CopyTargetContainer, true, true);443 444 // Local445 _helpSystem.InstallHelpContent(UpdatableHelpCommandType.UpdateHelpCommand, Context, tempContentPath,446 destPaths, module.GetHelpContentName(contentUri.Culture),447 Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName())),448 contentUri.Culture, xsdPath, out filesInstalled);449 }450 }451 catch (Exception e)452 {453 throw new UpdatableHelpSystemException("HelpContentNotFound", StringUtil.Format(HelpDisplayStrings.HelpContentNotFound),454 ErrorCategory.ResourceUnavailable, null, e);455 }456 }457 else458 {459 _helpSystem.InstallHelpContent(UpdatableHelpCommandType.UpdateHelpCommand, Context, helpContentUri,460 destPaths, module.GetHelpContentName(contentUri.Culture),461 Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetRandomFileName())),462 contentUri.Culture, xsdPath, out filesInstalled);463 }464 }465 else466 {467 // Remote468 469 // Download and install help content470 if (!_helpSystem.DownloadAndInstallHelpContent(UpdatableHelpCommandType.UpdateHelpCommand, Context,471 destPaths, module.GetHelpContentName(contentUri.Culture), contentUri.Culture, helpContentUri, xsdPath, out filesInstalled))472 {473 installed = false;474 continue;475 }476 }477 478 _helpSystem.GenerateHelpInfo(module.ModuleName, module.ModuleGuid, newHelpInfo.UnresolvedUri, contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture),479 moduleBase, module.GetHelpInfoName(), _force);480 481 foreach (string fileInstalled in filesInstalled)482 {483 WriteVerbose(StringUtil.Format(HelpDisplayStrings.SuccessfullyUpdatedHelpContent, module.ModuleName,484 StringUtil.Format(HelpDisplayStrings.UpdatedHelpContent, fileInstalled), contentUri.Culture.Name,485 newHelpInfo.GetCultureVersion(contentUri.Culture)));486 }487 488 LogMessage(StringUtil.Format(HelpDisplayStrings.UpdateHelpCompleted));489 490 installed = true;491 }492 catch (Exception e)493 {494 ProcessException(module.ModuleName, contentUri.Culture.Name, e);495 }496 }497 }498 499 return installed;500 }501 502 /// <summary>503 /// Throws PathMustBeValidContainers exception.504 /// </summary>505 /// <param name="path"></param>506 /// <param name="e"></param>507 private static void ThrowPathMustBeValidContainersException(string path, Exception e)508 {509 throw new UpdatableHelpSystemException("PathMustBeValidContainers",510 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument,511 null, e);512 }513 514 #endregion515 }516}517 