MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Diagnostics;9using System.Diagnostics.CodeAnalysis;10using System.IO;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 Save-Help cmdlet.19 /// </summary>20 [Cmdlet(VerbsData.Save, "Help", DefaultParameterSetName = SaveHelpCommand.PathParameterSetName,21 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096794")]22 public sealed class SaveHelpCommand : UpdatableHelpCommandBase23 {24 #region Constructor25 26 /// <summary>27 /// Class constructor.28 /// </summary>29 public SaveHelpCommand() : base(UpdatableHelpCommandType.SaveHelpCommand)30 {31 }32 33 #endregion34 35 private bool _alreadyCheckedOncePerDayPerModule = false;36 37 #region Parameters38 39 /// <summary>40 /// Specifies the paths to save updates to.41 /// </summary>42 [Parameter(Mandatory = true, Position = 0, ParameterSetName = PathParameterSetName)]43 [ValidateNotNull]44 [Alias("Path")]45 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]46 public string[] DestinationPath47 {48 get49 {50 return _path;51 }52 53 set54 {55 _path = value;56 }57 }58 59 private string[] _path;60 61 /// <summary>62 /// Specifies the literal path to save updates to.63 /// </summary>64 [Parameter(Mandatory = true, ParameterSetName = LiteralPathParameterSetName)]65 [Alias("PSPath", "LP")]66 [ValidateNotNull]67 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]68 public string[] LiteralPath69 {70 get71 {72 return _path;73 }74 75 set76 {77 _path = value;78 _isLiteralPath = true;79 }80 }81 82 private bool _isLiteralPath = false;83 84 /// <summary>85 /// Specifies the modules to update.86 /// </summary>87 [Parameter(Position = 1, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true, ParameterSetName = PathParameterSetName)]88 [Parameter(Position = 1, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true, ParameterSetName = LiteralPathParameterSetName)]89 [Alias("Name")]90 [ValidateNotNull]91 [ArgumentToModuleTransformation]92 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]93 public PSModuleInfo[] Module { get; set; }94 95 /// <summary>96 /// Specifies the Module Specifications to update.97 /// </summary>98 [Parameter(ParameterSetName = PathParameterSetName, ValueFromPipelineByPropertyName = true)]99 [Parameter(ParameterSetName = LiteralPathParameterSetName, ValueFromPipelineByPropertyName = true)]100 [ValidateNotNull]101 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]102 public ModuleSpecification[] FullyQualifiedModule { get; set; }103 104 #endregion105 106 #region Implementation107 108 /// <summary>109 /// </summary>110 protected override void ProcessRecord()111 {112 try113 {114 // Module and FullyQualifiedModule should not be specified at the same time.115 // Throw out terminating error if this is the case.116 if (Module != null && FullyQualifiedModule != null)117 {118 string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "Module", "FullyQualifiedModule");119 ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null);120 ThrowTerminatingError(error);121 }122 123 List<string> moduleNames = null;124 List<PSModuleInfo> moduleInfos = null;125 126 if (Module != null)127 {128 moduleNames = new List<string>();129 moduleInfos = new List<PSModuleInfo>();130 131 foreach (PSModuleInfo moduleInfo in Module)132 {133 // WinBlue: 268863134 // this check will cover the cases where135 // user supplied just name with -Module parameter.136 // In other cases, user must have supplied either137 // PSModuleInfo or Deserialized PSModuleInfo138 if (string.IsNullOrEmpty(moduleInfo.ModuleBase))139 {140 moduleNames.Add(moduleInfo.Name);141 }142 else143 {144 moduleInfos.Add(moduleInfo);145 }146 }147 }148 149 base.Process(moduleNames, FullyQualifiedModule);150 base.Process(moduleInfos);151 }152 finally153 {154 ProgressRecord progress = new ProgressRecord(activityId, HelpDisplayStrings.SaveProgressActivityForModule, HelpDisplayStrings.UpdateProgressInstalling);155 156 progress.PercentComplete = 100;157 progress.RecordType = ProgressRecordType.Completed;158 159 WriteProgress(progress);160 }161 }162 163 /// <summary>164 /// Process a single module with a given culture.165 /// </summary>166 /// <param name="module">Module to process.</param>167 /// <param name="culture">Culture to use.</param>168 /// <returns>True if the module has been processed, false if not.</returns>169 internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, string culture)170 {171 Collection<string> resolvedPaths = new Collection<string>();172 173 // Search for the HelpInfo XML174 foreach (string path in _path)175 {176 UpdatableHelpSystemDrive helpInfoDrive = null;177 178 try179 {180 if (string.IsNullOrEmpty(path))181 {182 PSArgumentException e = new PSArgumentException(StringUtil.Format(HelpDisplayStrings.PathNullOrEmpty));183 WriteError(e.ErrorRecord);184 return false;185 }186 187 string destPath = path;188 189 if (_credential != null)190 {191 if (path.Contains('*'))192 {193 // Deal with wildcards194 195 int index = path.IndexOf('*');196 197 if (index == 0)198 {199 throw new UpdatableHelpSystemException("PathMustBeValidContainers",200 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument,201 null, new ItemNotFoundException());202 }203 else204 {205 int i = index;206 for (; i >= 0; i--)207 {208 if (path[i].Equals('/') || path[i].Equals('\\'))209 {210 break;211 }212 }213 214 if (i == 0)215 {216 throw new UpdatableHelpSystemException("PathMustBeValidContainers",217 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument,218 null, new ItemNotFoundException());219 }220 221 helpInfoDrive = new UpdatableHelpSystemDrive(this, path.Substring(0, i), _credential);222 destPath = Path.Combine(helpInfoDrive.DriveName, path.Substring(i + 1, path.Length - (i + 1)));223 }224 }225 else226 {227 helpInfoDrive = new UpdatableHelpSystemDrive(this, path, _credential);228 destPath = helpInfoDrive.DriveName;229 }230 }231 232 if (_isLiteralPath)233 {234 string destinationPath = GetUnresolvedProviderPathFromPSPath(destPath);235 if (!Directory.Exists(destinationPath))236 {237 throw new UpdatableHelpSystemException("PathMustBeValidContainers",238 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument,239 null, new ItemNotFoundException());240 }241 242 resolvedPaths.Add(destinationPath);243 }244 else245 {246 try247 {248 // Expand wildcard characters249 foreach (string tempPath in ResolvePath(destPath, false, false))250 {251 resolvedPaths.Add(tempPath);252 }253 }254 catch (ItemNotFoundException e)255 {256 throw new UpdatableHelpSystemException("PathMustBeValidContainers",257 StringUtil.Format(HelpDisplayStrings.PathMustBeValidContainers, path), ErrorCategory.InvalidArgument, null, e);258 }259 }260 }261 finally262 {263 helpInfoDrive?.Dispose();264 }265 }266 267 if (resolvedPaths.Count == 0)268 {269 return true;270 }271 272 bool installed = false;273 274 foreach (string path in resolvedPaths)275 {276 UpdatableHelpInfo currentHelpInfo = null;277 UpdatableHelpInfo newHelpInfo = null;278 string helpInfoUri = null;279 280 // if -force is specified, no need to read from the current HelpInfo.xml file281 // because it won't be used for checking "IsUpdateNecessary"282 string xml = _force283 ? null284 : UpdatableHelpSystem.LoadStringFromPath(this,285 SessionState.Path.Combine(path, module.GetHelpInfoName()),286 _credential);287 288 if (xml != null)289 {290 // constructing the helpinfo object from previous update help log xml..291 // no need to resolve the uri's in this case.292 currentHelpInfo = _helpSystem.CreateHelpInfo(xml, module.ModuleName, module.ModuleGuid,293 currentCulture: null, pathOverride: null, verbose: false,294 shouldResolveUri: false, ignoreValidationException: false);295 }296 297 // Don't update too frequently298 if (!_alreadyCheckedOncePerDayPerModule && !CheckOncePerDayPerModule(module.ModuleName, path, module.GetHelpInfoName(), DateTime.UtcNow, _force))299 {300 return true;301 }302 303 _alreadyCheckedOncePerDayPerModule = true;304 305 // Form the actual HelpInfo.xml uri306 helpInfoUri = _helpSystem.GetHelpInfoUri(module, null).ResolvedUri;307 string uri = helpInfoUri + module.GetHelpInfoName();308 309 newHelpInfo = _helpSystem.GetHelpInfo(_commandType, uri, module.ModuleName, module.ModuleGuid, culture);310 311 if (newHelpInfo == null)312 {313 throw new UpdatableHelpSystemException("UnableToRetrieveHelpInfoXml",314 StringUtil.Format(HelpDisplayStrings.UnableToRetrieveHelpInfoXml, culture), ErrorCategory.ResourceUnavailable,315 null, null);316 }317 318 string tempPath = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Path.GetTempFileName()));319 foreach (UpdatableHelpUri contentUri in newHelpInfo.HelpContentUriCollection)320 {321 if (!IsUpdateNecessary(module, currentHelpInfo, newHelpInfo, contentUri.Culture, _force))322 {323 WriteVerbose(StringUtil.Format(HelpDisplayStrings.SuccessfullyUpdatedHelpContent, module.ModuleName, HelpDisplayStrings.NewestContentAlreadyDownloaded,324 contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture)));325 326 installed = true;327 continue;328 }329 else330 {331 Debug.Assert(helpInfoUri != null, "If we are here, helpInfoUri must not be null");332 333 string helpContentUri = contentUri.ResolvedUri;334 string helpContentName = module.GetHelpContentName(contentUri.Culture);335 336 UpdatableHelpSystemDrive helpContentDrive = null;337 338 try339 {340 if (Directory.Exists(helpContentUri))341 {342 File.Copy(SessionState.Path.Combine(helpContentUri, helpContentName),343 SessionState.Path.Combine(path, helpContentName), true);344 }345 else346 {347 // Remote348 349 if (_credential != null)350 {351 try352 {353 helpContentDrive = new UpdatableHelpSystemDrive(this, path, _credential);354 355 if (!_helpSystem.DownloadHelpContent(_commandType, tempPath, helpContentUri, helpContentName, culture))356 {357 installed = false;358 continue;359 }360 361 InvokeProvider.Item.Copy(new string[1] { tempPath }, helpContentDrive.DriveName, true, CopyContainers.CopyChildrenOfTargetContainer,362 true, true);363 }364 catch (Exception e)365 {366 ProcessException(module.ModuleName, contentUri.Culture.Name, e);367 installed = false;368 continue;369 }370 }371 else372 {373 if (!_helpSystem.DownloadHelpContent(_commandType, path, helpContentUri, helpContentName, culture))374 {375 installed = false;376 continue;377 }378 }379 }380 381 if (_credential != null)382 {383 _helpSystem.GenerateHelpInfo(module.ModuleName, module.ModuleGuid, newHelpInfo.UnresolvedUri, contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture), tempPath,384 module.GetHelpInfoName(), _force);385 386 InvokeProvider.Item.Copy(new string[1] { Path.Combine(tempPath, module.GetHelpInfoName()) }, Path.Combine(helpContentDrive.DriveName, module.GetHelpInfoName()), false,387 CopyContainers.CopyTargetContainer, true, true);388 }389 else390 {391 _helpSystem.GenerateHelpInfo(module.ModuleName, module.ModuleGuid, newHelpInfo.UnresolvedUri, contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture), path,392 module.GetHelpInfoName(), _force);393 }394 395 WriteVerbose(StringUtil.Format(HelpDisplayStrings.SuccessfullyUpdatedHelpContent, module.ModuleName,396 StringUtil.Format(HelpDisplayStrings.SavedHelpContent, System.IO.Path.Combine(path, helpContentName)), contentUri.Culture.Name,397 newHelpInfo.GetCultureVersion(contentUri.Culture)));398 399 LogMessage(StringUtil.Format(HelpDisplayStrings.SaveHelpCompleted, path));400 }401 catch (Exception e)402 {403 ProcessException(module.ModuleName, contentUri.Culture.Name, e);404 }405 finally406 {407 helpContentDrive?.Dispose();408 }409 }410 }411 412 installed = true;413 }414 415 return installed;416 }417 418 #endregion419 }420 421 internal sealed class ArgumentToModuleTransformationAttribute : ArgumentTransformationAttribute422 {423 public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)424 {425 object argument = PSObject.Base(inputData);426 427 // deal with scalar string argument428 var strArg = argument as string;429 if (strArg != null)430 {431 return new PSModuleInfo(name: strArg, path: null, context: null, sessionState: null);432 }433 434 // deal with IList argument435 IList iListArg = ParameterBinderBase.GetIList(argument);436 if (iListArg != null && iListArg.Count > 0)437 {438 int elementCount = iListArg.Count;439 int targetIndex = 0;440 var target = Array.CreateInstance(typeof(object), elementCount);441 442 foreach (object element in iListArg)443 {444 var elementValue = PSObject.Base(element);445 446 if (elementValue is PSModuleInfo)447 {448 target.SetValue(elementValue, targetIndex++);449 }450 else if (elementValue is string)451 {452 var elementAsModuleObj = new PSModuleInfo(name: (string)elementValue, path: null, context: null, sessionState: null);453 target.SetValue(elementAsModuleObj, targetIndex++);454 }455 else456 {457 PSModuleInfo elementValueModuleInfo = null;458 if (TryConvertFromDeserializedModuleInfo(elementValue, out elementValueModuleInfo))459 {460 target.SetValue(elementValueModuleInfo, targetIndex++);461 }462 else463 {464 target.SetValue(element, targetIndex++);465 }466 }467 }468 469 return target;470 }471 472 PSModuleInfo moduleInfo = null;473 if (TryConvertFromDeserializedModuleInfo(inputData, out moduleInfo))474 {475 return moduleInfo;476 }477 478 return inputData;479 }480 481 private static bool TryConvertFromDeserializedModuleInfo(object inputData, out PSModuleInfo moduleInfo)482 {483 moduleInfo = null;484 PSObject pso = inputData as PSObject;485 if (Deserializer.IsDeserializedInstanceOfType(pso, typeof(PSModuleInfo)))486 {487 string moduleName;488 LanguagePrimitives.TryConvertTo<string>(pso.Properties["Name"].Value, out moduleName);489 490 Guid moduleGuid;491 LanguagePrimitives.TryConvertTo<Guid>(pso.Properties["Guid"].Value, out moduleGuid);492 493 Version moduleVersion;494 LanguagePrimitives.TryConvertTo<Version>(pso.Properties["Version"].Value, out moduleVersion);495 496 string helpInfoUri;497 LanguagePrimitives.TryConvertTo<string>(pso.Properties["HelpInfoUri"].Value, out helpInfoUri);498 499 moduleInfo = new PSModuleInfo(name: moduleName, path: null, context: null, sessionState: null);500 moduleInfo.SetGuid(moduleGuid);501 moduleInfo.SetVersion(moduleVersion);502 moduleInfo.SetHelpInfoUri(helpInfoUri);503 // setting the base to temp directory as this is a deserialized504 // module info.505 moduleInfo.SetModuleBase(System.IO.Path.GetTempPath());506 507 return true;508 }509 510 return false;511 }512 }513}514 