MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Collections.Specialized;8using System.Collections.ObjectModel;9using System.Diagnostics.CodeAnalysis;10using System.Globalization;11using System.IO;12using System.Linq;13using System.Management.Automation.Internal;14using System.Management.Automation.Language;15using System.Management.Automation.Remoting;16using System.Management.Automation.Runspaces;17using System.Management.Automation.Tracing;18using System.Net.Mail;19using System.Reflection;20using System.Runtime.CompilerServices;21using System.Security;22using System.Text;23using System.Xml;24 25using Microsoft.Management.Infrastructure;26using Microsoft.Management.Infrastructure.Serialization;27using Microsoft.PowerShell.Commands;28 29using Dbg = System.Management.Automation.Diagnostics;30 31namespace System.Management.Automation32{33 [Flags]34 internal enum SerializationOptions35 {36 None = 0,37 UseDepthFromTypes = 1,38 NoRootElement = 2,39 NoNamespace = 4,40 NoObjectRefIds = 8,41 PreserveSerializationSettingOfOriginal = 16,42 43 RemotingOptions = UseDepthFromTypes | NoRootElement | NoNamespace | PreserveSerializationSettingOfOriginal,44 }45 46 internal class SerializationContext47 {48 private const int DefaultSerializationDepth = 2;49 50 internal SerializationContext()51 : this(DefaultSerializationDepth, true)52 {53 }54 55 internal SerializationContext(int depth, bool useDepthFromTypes)56 : this(57 depth,58 (useDepthFromTypes ? SerializationOptions.UseDepthFromTypes : SerializationOptions.None) |59 SerializationOptions.PreserveSerializationSettingOfOriginal,60 null)61 {62 }63 64 internal SerializationContext(int depth, SerializationOptions options, PSRemotingCryptoHelper cryptoHelper)65 {66 if (depth < 1)67 {68 throw PSTraceSource.NewArgumentException("writer", Serialization.DepthOfOneRequired);69 }70 71 this.depth = depth;72 this.options = options;73 this.cryptoHelper = cryptoHelper;74 }75 76 internal readonly int depth;77 internal readonly SerializationOptions options;78 internal readonly PSRemotingCryptoHelper cryptoHelper;79 80 internal readonly CimClassSerializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassSerializationCache<CimClassSerializationId>();81 }82 83 /// <summary>84 /// This class provides public functionality for serializing a PSObject.85 /// </summary>86 public static class PSSerializer87 {88 /// <summary>89 /// Serializes an object into PowerShell CliXml.90 /// </summary>91 /// <param name="source">The input object to serialize. Serializes to a default depth of 1.</param>92 /// <returns>The serialized object, as CliXml.</returns>93 public static string Serialize(object source)94 {95 return Serialize(source, s_mshDefaultSerializationDepth);96 }97 98 /// <summary>99 /// Serializes an object into PowerShell CliXml.100 /// </summary>101 /// <param name="source">The input object to serialize.</param>102 /// <param name="depth">The depth of the members to serialize.</param>103 /// <returns>The serialized object, as CliXml.</returns>104 public static string Serialize(object source, int depth)105 {106 // Create an xml writer107 StringBuilder sb = new StringBuilder();108 XmlWriterSettings xmlSettings = new XmlWriterSettings();109 xmlSettings.CloseOutput = true;110 xmlSettings.Encoding = System.Text.Encoding.Unicode;111 xmlSettings.Indent = true;112 xmlSettings.OmitXmlDeclaration = true;113 XmlWriter xw = XmlWriter.Create(sb, xmlSettings);114 115 // Serialize the objects116 Serializer serializer = new Serializer(xw, depth, true);117 serializer.Serialize(source);118 serializer.Done();119 serializer = null;120 121 // Return the output122 return sb.ToString();123 }124 125 /// <summary>126 /// Serializes list of objects into PowerShell CliXml.127 /// </summary>128 /// <param name="source">The input objects to serialize.</param>129 /// <param name="depth">The depth of the members to serialize.</param>130 /// <param name="enumerate">Enumerates input objects and serializes one at a time.</param>131 /// <returns>The serialized object, as CliXml.</returns>132 internal static string Serialize(IList<object> source, int depth, bool enumerate)133 {134 StringBuilder sb = new();135 136 XmlWriterSettings xmlSettings = new()137 {138 CloseOutput = true,139 Encoding = Encoding.Unicode,140 Indent = true,141 OmitXmlDeclaration = true142 };143 144 XmlWriter xw = XmlWriter.Create(sb, xmlSettings);145 Serializer serializer = new(xw, depth, useDepthFromTypes: true);146 147 if (enumerate)148 {149 foreach (object item in source)150 {151 serializer.Serialize(item);152 }153 }154 else155 {156 serializer.Serialize(source);157 }158 159 serializer.Done();160 161 return sb.ToString();162 }163 164 /// <summary>165 /// Deserializes PowerShell CliXml into an object.166 /// </summary>167 /// <param name="source">The CliXml the represents the object to deserialize.</param>168 /// <returns>An object that represents the serialized content.</returns>169 public static object Deserialize(string source)170 {171 object[] results = DeserializeAsList(source);172 173 // Return the results174 if (results.Length == 0)175 {176 return null;177 }178 else if (results.Length == 1)179 {180 return results[0];181 }182 else183 {184 return results;185 }186 }187 188 /// <summary>189 /// Deserializes PowerShell CliXml into a list of objects.190 /// </summary>191 /// <param name="source">The CliXml the represents the object to deserialize.</param>192 /// <returns>An object array represents the serialized content.</returns>193 public static object[] DeserializeAsList(string source)194 {195 List<object> results = new List<object>();196 197 // Create the text reader to hold the content198 TextReader textReader = new StringReader(source);199 XmlReader xmlReader = XmlReader.Create(textReader, InternalDeserializer.XmlReaderSettingsForCliXml);200 201 // Deserialize the content202 Deserializer deserializer = new Deserializer(xmlReader);203 while (!deserializer.Done())204 {205 object result = deserializer.Deserialize();206 results.Add(result);207 }208 209 return results.ToArray();210 }211 212 /// <summary>213 /// Default depth of serialization.214 /// </summary>215 private static readonly int s_mshDefaultSerializationDepth = 1;216 }217 218 /// <summary>219 /// This class provides functionality for serializing a PSObject.220 /// </summary>221 internal class Serializer222 {223 #region constructor224 225 private readonly InternalSerializer _serializer;226 227 /// <summary>228 /// Creates a Serializer using default serialization context.229 /// </summary>230 /// <param name="writer">Writer to be used for serialization.</param>231 internal Serializer(XmlWriter writer)232 : this(writer, new SerializationContext())233 {234 }235 236 /// <summary>237 /// Creates a Serializer using specified serialization context.238 /// </summary>239 /// <param name="writer">Writer to be used for serialization.</param>240 /// <param name="depth">Depth of serialization.</param>241 /// <param name="useDepthFromTypes">242 /// if <see langword="true"/> then types.ps1xml can override depth243 /// for a particular types (using SerializationDepth property)244 /// </param>245 internal Serializer(XmlWriter writer, int depth, bool useDepthFromTypes)246 : this(writer, new SerializationContext(depth, useDepthFromTypes))247 {248 }249 250 /// <summary>251 /// Creates a Serializer using specified serialization context.252 /// </summary>253 /// <param name="writer">Writer to be used for serialization.</param>254 /// <param name="context">Serialization context.</param>255 internal Serializer(XmlWriter writer, SerializationContext context)256 {257 if (writer == null)258 {259 throw PSTraceSource.NewArgumentException(nameof(writer));260 }261 262 if (context == null)263 {264 throw PSTraceSource.NewArgumentException(nameof(context));265 }266 267 _serializer = new InternalSerializer(writer, context);268 _serializer.Start();269 }270 271 #endregion constructor272 273 #region public methods / properties274 275 /// <summary>276 /// Used by Remoting infrastructure. This TypeTable instance277 /// will be used by Serializer if ExecutionContext is not278 /// available (to get the ExecutionContext's TypeTable)279 /// </summary>280 internal TypeTable TypeTable281 {282 get { return _serializer.TypeTable; }283 284 set { _serializer.TypeTable = value; }285 }286 287 /// <summary>288 /// Serializes the object.289 /// </summary>290 /// <param name="source">Object to be serialized.</param>291 /// <remarks>292 /// Please note that this method shouldn't throw any exceptions.293 /// If it throws - please open a bug.294 /// </remarks>295 internal void Serialize(object source)296 {297 Serialize(source, null);298 }299 300 /// <summary>301 /// Serializes passed in object.302 /// </summary>303 /// <param name="source">304 /// object to be serialized305 /// </param>306 /// <param name="streamName">307 /// Stream to which this object belong. Ex: Output, Error etc.308 /// </param>309 /// <remarks>310 /// Please note that this method shouldn't throw any exceptions.311 /// If it throws - please open a bug.312 /// </remarks>313 internal void Serialize(object source, string streamName)314 {315 _serializer.WriteOneTopLevelObject(source, streamName);316 }317 318 /// <summary>319 /// Write the end of root element.320 /// </summary>321 internal void Done()322 {323 _serializer.End();324 }325 326 internal void Stop()327 {328 _serializer.Stop();329 }330 331 #endregion332 }333 334 [Flags]335 internal enum DeserializationOptions336 {337 None = 0,338 NoRootElement = 256, // I start at 256 to try not to overlap339 NoNamespace = 512, // with SerializationOptions and to catch bugs early340 DeserializeScriptBlocks = 1024,341 342 RemotingOptions = NoRootElement | NoNamespace,343 }344 345 internal class DeserializationContext346 {347 internal DeserializationContext()348 : this(DeserializationOptions.None, null)349 {350 }351 352 internal DeserializationContext(DeserializationOptions options, PSRemotingCryptoHelper cryptoHelper)353 {354 this.options = options;355 this.cryptoHelper = cryptoHelper;356 }357 358 /// <summary>359 /// Limits the total data processed by the deserialization context. Deserialization context360 /// is used by PriorityReceivedDataCollection (remoting) to process incoming data from the361 /// remote end. A value of Null means that the max memory is unlimited.362 /// </summary>363 internal int? MaximumAllowedMemory { get; set; }364 365 /// <summary>366 /// Logs that memory used by deserialized objects is not related to the size of input xml.367 /// Used mainly to account for memory usage of cloned TypeNames when calculating memory quota usage.368 /// </summary>369 /// <param name="amountOfExtraMemory"></param>370 internal void LogExtraMemoryUsage(int amountOfExtraMemory)371 {372 if (amountOfExtraMemory < 0)373 {374 return;375 }376 377 if (MaximumAllowedMemory.HasValue)378 {379 if (amountOfExtraMemory > (MaximumAllowedMemory.Value - _totalDataProcessedSoFar))380 {381 string message = StringUtil.Format(Serialization.DeserializationMemoryQuota, ((double)MaximumAllowedMemory.Value) / (1 << 20),382 ConfigurationDataFromXML.MAXRCVDOBJSIZETOKEN_CamelCase,383 ConfigurationDataFromXML.MAXRCVDCMDSIZETOKEN_CamelCase);384 throw new XmlException(message);385 }386 387 _totalDataProcessedSoFar += amountOfExtraMemory;388 }389 }390 391 private int _totalDataProcessedSoFar;392 393 internal readonly DeserializationOptions options;394 internal readonly PSRemotingCryptoHelper cryptoHelper;395 396 internal static readonly int MaxItemsInCimClassCache = 100;397 internal readonly CimClassDeserializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassDeserializationCache<CimClassSerializationId>();398 }399 400 internal class CimClassDeserializationCache<TKey>401 {402 private readonly Dictionary<TKey, CimClass> _cimClassIdToClass = new Dictionary<TKey, CimClass>();403 404 internal void AddCimClassToCache(TKey key, CimClass cimClass)405 {406 if (_cimClassIdToClass.Count >= DeserializationContext.MaxItemsInCimClassCache)407 {408 _cimClassIdToClass.Clear();409 }410 411 _cimClassIdToClass.Add(key, cimClass);412 413 /* PRINTF DEBUG414 Console.WriteLine("Contents of deserialization cache (after a call to AddCimClassToCache ({0})):", key);415 Console.WriteLine(" Count = {0}", this._cimClassIdToClass.Count);416 foreach (var t in this._cimClassIdToClass.Keys)417 {418 Console.WriteLine(" {0}", t);419 }420 */421 }422 423 internal CimClass GetCimClassFromCache(TKey key)424 {425 CimClass cimClass;426 if (_cimClassIdToClass.TryGetValue(key, out cimClass))427 {428 /* PRINTF DEBUG429 Console.WriteLine("GetCimClassFromCache - class found: {0}", key);430 */431 432 return cimClass;433 }434 435 /* PRINTF DEBUG436 Console.WriteLine("GetCimClassFromCache - class NOT found: {0}", key);437 */438 439 return null;440 }441 }442 443 internal class CimClassSerializationCache<TKey>444 {445 private readonly HashSet<TKey> _cimClassesHeldByDeserializer = new HashSet<TKey>(EqualityComparer<TKey>.Default);446 447 internal bool DoesDeserializerAlreadyHaveCimClass(TKey key)448 {449 return _cimClassesHeldByDeserializer.Contains(key);450 }451 452 internal void AddClassToCache(TKey key)453 {454 Dbg.Assert(!_cimClassesHeldByDeserializer.Contains(key), "This method should not be called for classes already in the cache");455 456 if (_cimClassesHeldByDeserializer.Count >= DeserializationContext.MaxItemsInCimClassCache)457 {458 _cimClassesHeldByDeserializer.Clear();459 }460 461 _cimClassesHeldByDeserializer.Add(key);462 463 /* PRINTF DEBUG464 Console.WriteLine("Contents of serialization cache (after adding {0}):", key);465 Console.WriteLine(" Count = {0}", this._cimClassesHeldByDeserializer.Count);466 foreach (var t in _cimClassesHeldByDeserializer)467 {468 Console.WriteLine(" {0}", t);469 }470 */471 }472 }473 474 internal class CimClassSerializationId : Tuple<string, string, string, int>475 {476 public CimClassSerializationId(string className, string namespaceName, string computerName, int hashCode)477 : base(className, namespaceName, computerName, hashCode)478 {479 }480 481 public string ClassName { get { return this.Item1; } }482 483 public string NamespaceName { get { return this.Item2; } }484 485 public string ComputerName { get { return this.Item3; } }486 487 public int ClassHashCode { get { return this.Item4; } }488 }489 490 /// <summary>491 /// This class provides functionality for deserializing a PSObject.492 /// </summary>493 internal class Deserializer494 {495 #region constructor496 497 private readonly XmlReader _reader;498 private readonly InternalDeserializer _deserializer;499 private readonly DeserializationContext _context;500 501 /// <summary>502 /// Creates a Deserializer using default deserialization context.503 /// </summary>504 /// <param name="reader">Reader to be used for deserialization.</param>505 /// <exception cref="XmlException">506 /// Thrown when the xml is in an incorrect format507 /// </exception>508 internal Deserializer(XmlReader reader)509 : this(reader, new DeserializationContext())510 {511 }512 513 /// <summary>514 /// Creates a Deserializer using specified serialization context.515 /// </summary>516 /// <param name="reader">Reader to be used for deserialization.</param>517 /// <param name="context">Serialization context.</param>518 /// <exception cref="XmlException">519 /// Thrown when the xml is in an incorrect format520 /// </exception>521 internal Deserializer(XmlReader reader, DeserializationContext context)522 {523 if (reader == null)524 {525 throw PSTraceSource.NewArgumentNullException(nameof(reader));526 }527 528 _reader = reader;529 _context = context;530 _deserializer = new InternalDeserializer(_reader, _context);531 532 try533 {534 Start();535 }536 catch (XmlException exception)537 {538 ReportExceptionForETW(exception);539 throw;540 }541 }542 543 #endregion constructor544 545 #region public method / properties546 547 private static void ReportExceptionForETW(XmlException exception)548 {549 PSEtwLog.LogAnalyticError(550 PSEventId.Serializer_XmlExceptionWhenDeserializing, PSOpcode.Exception, PSTask.Serialization,551 PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,552 exception.LineNumber, exception.LinePosition, exception.ToString());553 }554 555 private bool _done = false;556 557 /// <summary>558 /// Used by Remoting infrastructure. This TypeTable instance559 /// will be used by Deserializer if ExecutionContext is not560 /// available (to get the ExecutionContext's TypeTable)561 /// </summary>562 internal TypeTable TypeTable563 {564 get { return _deserializer.TypeTable; }565 566 set { _deserializer.TypeTable = value; }567 }568 569 /// <summary>570 /// Read the root element tag and set the cursor to start tag of571 /// first object.572 /// </summary>573 private void Start()574 {575 Dbg.Assert(_reader.ReadState == ReadState.Initial, "When deserialization starts we should have XmlReader.ReadState == Initial");576 Dbg.Assert(_reader.NodeType == XmlNodeType.None, "When deserialization starts we should have XmlReader.NodeType == None");577 _reader.Read();578 579 // If version is not provided, we assume it is the default580 string version = InternalSerializer.DefaultVersion;581 if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)582 {583 _done = _reader.EOF;584 }585 else586 {587 // Make sure the reader is positioned on the root ( <Objs> ) element (not on XmlDeclaration for example)588 _reader.MoveToContent();589 Dbg.Assert(_reader.EOF || (_reader.NodeType == XmlNodeType.Element), "When deserialization starts reading we should have XmlReader.NodeType == Element");590 591 // Read version attribute and validate it.592 string versionAttribute = _reader.GetAttribute(SerializationStrings.VersionAttribute);593 if (versionAttribute != null)594 {595 version = versionAttribute;596 }597 598 // If the root element tag is empty, there are no objects to read.599 if (!_deserializer.ReadStartElementAndHandleEmpty(SerializationStrings.RootElementTag))600 {601 _done = true;602 }603 }604 605 _deserializer.ValidateVersion(version);606 }607 608 internal bool Done()609 {610 if (!_done)611 {612 if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)613 {614 _done = _reader.EOF;615 }616 else617 {618 if (_reader.NodeType == XmlNodeType.EndElement)619 {620 try621 {622 _reader.ReadEndElement();623 }624 catch (XmlException exception)625 {626 ReportExceptionForETW(exception);627 throw;628 }629 630 _done = true;631 }632 }633 }634 635 return _done;636 }637 638 internal void Stop()639 {640 _deserializer.Stop();641 }642 643 /// <summary>644 /// Deserializes next object.645 /// </summary>646 /// <exception cref="XmlException">647 /// Thrown when the xml is in an incorrect format648 /// </exception>649 internal object Deserialize()650 {651 string ignore;652 return Deserialize(out ignore);653 }654 655 /// <summary>656 /// Deserializes next object.657 /// </summary>658 /// <param name="streamName">Stream the object belongs to (i.e. "Error", "Output", etc.).</param>659 /// <exception cref="XmlException">660 /// Thrown when the xml is in an incorrect format661 /// </exception>662 internal object Deserialize(out string streamName)663 {664 if (Done())665 {666 throw PSTraceSource.NewInvalidOperationException(Serialization.ReadCalledAfterDone);667 }668 669 try670 {671 return _deserializer.ReadOneObject(out streamName);672 }673 catch (XmlException exception)674 {675 ReportExceptionForETW(exception);676 throw;677 }678 }679 680 #endregion public methods681 682 #region Helper methods for dealing with "Deserialized." prefix683 684 /// <summary>685 /// Adds "Deserialized." prefix to passed in argument if not already present.686 /// </summary>687 /// <param name="type"></param>688 internal static void AddDeserializationPrefix(ref string type)689 {690 Dbg.Assert(type != null, "caller should validate the parameter");691 if (!type.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))692 {693 type = type.Insert(0, Deserializer.DeserializationTypeNamePrefix);694 }695 }696 697 /// <summary>698 /// Checks if an object <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses.699 /// </summary>700 /// <param name="o"></param>701 /// <param name="type"></param>702 /// <returns><see langword="true"/> if <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>703 internal static bool IsInstanceOfType(object o, Type type)704 {705 if (type == null)706 {707 throw PSTraceSource.NewArgumentNullException(nameof(type));708 }709 710 if (o == null)711 {712 return false;713 }714 715 return type.IsInstanceOfType(PSObject.Base(o)) || IsDeserializedInstanceOfType(o, type);716 }717 718 /// <summary>719 /// Checks if an object <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses.720 /// </summary>721 /// <param name="o"></param>722 /// <param name="type"></param>723 /// <returns><see langword="true"/> if <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>724 internal static bool IsDeserializedInstanceOfType(object o, Type type)725 {726 if (type == null)727 {728 throw PSTraceSource.NewArgumentNullException(nameof(type));729 }730 731 if (o == null)732 {733 return false;734 }735 736 PSObject pso = o as PSObject;737 if (pso != null)738 {739 IEnumerable<string> typeNames = pso.InternalTypeNames;740 if (typeNames != null)741 {742 foreach (string typeName in typeNames)743 {744 if (typeName.Length == Deserializer.DeserializationTypeNamePrefix.Length + type.FullName.Length &&745 typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase) &&746 typeName.EndsWith(type.FullName, StringComparison.OrdinalIgnoreCase))747 {748 return true;749 }750 }751 }752 }753 754 // not the right type755 return false;756 }757 758 internal static string MaskDeserializationPrefix(string typeName)759 {760 if (typeName == null)761 {762 return null;763 }764 765 if (typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))766 {767 typeName = typeName.Substring(Deserializer.DeserializationTypeNamePrefix.Length);768 }769 770 return typeName;771 }772 773 /// <summary>774 /// Gets a new collection of typenames without "Deserialization." prefix775 /// in the typename. This will allow to map type info/format info of the original type776 /// for deserialized objects.777 /// </summary>778 /// <param name="typeNames"></param>779 /// <returns>780 /// Null if no type with "Deserialized." prefix is found.781 /// Otherwise <paramref name="typeNames"/> with the prefix removed if any.782 /// </returns>783 internal static Collection<string> MaskDeserializationPrefix(Collection<string> typeNames)784 {785 Dbg.Assert(typeNames != null, "typeNames cannot be null");786 787 bool atleastOneDeserializedTypeFound = false;788 789 Collection<string> typesWithoutPrefix = new Collection<string>();790 foreach (string type in typeNames)791 {792 if (type.StartsWith(Deserializer.DeserializationTypeNamePrefix,793 StringComparison.OrdinalIgnoreCase))794 {795 atleastOneDeserializedTypeFound = true;796 // remove *only* the prefix797 typesWithoutPrefix.Add(type.Substring(Deserializer.DeserializationTypeNamePrefix.Length));798 }799 else800 {801 typesWithoutPrefix.Add(type);802 }803 }804 805 if (atleastOneDeserializedTypeFound)806 {807 return typesWithoutPrefix;808 }809 810 return null;811 }812 813 /// <summary>814 /// Used to prefix a typename for deserialization.815 /// </summary>816 private const string DeserializationTypeNamePrefix = "Deserialized.";817 818 #endregion819 }820 821 /// <summary>822 /// Types of known type container supported by monad.823 /// </summary>824 internal enum ContainerType825 {826 Dictionary,827 Queue,828 Stack,829 List,830 Enumerable,831 None832 }833 834 /// <summary>835 /// This internal helper class provides methods for serializing mshObject.836 /// </summary>837 internal class InternalSerializer838 {839 #region constructor840 841 internal const string DefaultVersion = "1.1.0.1";842 843 /// <summary>844 /// Xml writer to be used.845 /// </summary>846 private readonly XmlWriter _writer;847 848 /// <summary>849 /// Serialization context.850 /// </summary>851 private readonly SerializationContext _context;852 853 /// Used by Remoting infrastructure. This TypeTable instance854 /// will be used by Serializer if ExecutionContext is not855 /// available (to get the ExecutionContext's TypeTable)856 private TypeTable _typeTable;857 858 /// <summary>859 /// Depth below top level - used to prevent infinitely deep serialization860 /// (without this protection it would be possible i.e. with SerializationDepth and recursion)861 /// </summary>862 private int _depthBelowTopLevel;863 864 private const int MaxDepthBelowTopLevel = 50;865 866 private readonly ReferenceIdHandlerForSerializer<object> _objectRefIdHandler;867 private readonly ReferenceIdHandlerForSerializer<ConsolidatedString> _typeRefIdHandler;868 869 internal InternalSerializer(XmlWriter writer, SerializationContext context)870 {871 Dbg.Assert(writer != null, "caller should validate the parameter");872 Dbg.Assert(context != null, "caller should validate the parameter");873 _writer = writer;874 _context = context;875 876 IDictionary<object, ulong> objectRefIdDictionary = null;877 if ((_context.options & SerializationOptions.NoObjectRefIds) == 0)878 {879 objectRefIdDictionary = new WeakReferenceDictionary<UInt64>();880 }881 882 _objectRefIdHandler = new ReferenceIdHandlerForSerializer<object>(objectRefIdDictionary);883 884 _typeRefIdHandler = new ReferenceIdHandlerForSerializer<ConsolidatedString>(885 new Dictionary<ConsolidatedString, UInt64>(ConsolidatedString.EqualityComparer));886 }887 888 #endregion889 890 /// <summary>891 /// Used by Remoting infrastructure. This TypeTable instance892 /// will be used by Serializer if ExecutionContext is not893 /// available (to get the ExecutionContext's TypeTable)894 /// </summary>895 internal TypeTable TypeTable896 {897 get { return _typeTable; }898 899 set { _typeTable = value; }900 }901 902 /// <summary>903 /// Writes the start of root element.904 /// </summary>905 internal void Start()906 {907 if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)908 {909 this.WriteStartElement(SerializationStrings.RootElementTag);910 this.WriteAttribute(SerializationStrings.VersionAttribute, InternalSerializer.DefaultVersion);911 }912 }913 914 /// <summary>915 /// Writes the end of root element.916 /// </summary>917 internal void End()918 {919 if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)920 {921 _writer.WriteEndElement();922 }923 924 _writer.Flush();925 }926 927 private bool _isStopping = false;928 929 /// <summary>930 /// Called from a separate thread will stop the serialization process.931 /// </summary>932 internal void Stop()933 {934 _isStopping = true;935 }936 937 private void CheckIfStopping()938 {939 if (_isStopping)940 {941 throw PSTraceSource.NewInvalidOperationException(Serialization.Stopping);942 }943 }944 945 internal static bool IsPrimitiveKnownType(Type input)946 {947 // Check if source is of primitive known type948 TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(input);949 return (pktInfo != null);950 }951 952 /// <summary>953 /// This writes one object.954 /// </summary>955 /// <param name="source">956 /// source to be serialized.957 /// </param>958 /// <param name="streamName">959 /// Stream to which source belongs960 /// </param>961 internal void WriteOneTopLevelObject962 (963 object source,964 string streamName965 )966 {967 Dbg.Assert(_depthBelowTopLevel == 0, "InternalSerializer.depthBelowTopLevel should be 0 at top-level");968 WriteOneObject(source, streamName, null, _context.depth);969 }970 971 private void WriteOneObject972 (973 object source,974 string streamName,975 string property,976 int depth977 )978 {979 Dbg.Assert(depth >= 0, "depth should always be greater or equal to zero");980 this.CheckIfStopping();981 982 if (source == null)983 {984 WriteNull(streamName, property);985 return;986 }987 988 try989 {990 _depthBelowTopLevel++;991 Dbg.Assert(_depthBelowTopLevel <= MaxDepthBelowTopLevel, "depthBelowTopLevel should be <= MaxDepthBelowTopLevel");992 if (HandleMaxDepth(source, streamName, property))993 {994 return;995 }996 997 depth = GetDepthOfSerialization(source, depth);998 999 if (HandlePrimitiveKnownTypeByConvertingToPSObject(source, streamName, property, depth))1000 {1001 return;1002 }1003 1004 // Object is not of primitive known type. Check if this has1005 // already been serialized.1006 string refId = _objectRefIdHandler.GetRefId(source);1007 if (refId != null)1008 {1009 WritePSObjectReference(streamName, property, refId);1010 return;1011 }1012 1013 if (HandlePrimitiveKnownTypePSObject(source, streamName, property, depth))1014 {1015 return;1016 }1017 1018 // Note: We do not use containers in depth calculation. i.e even if the1019 // current depth is zero, we serialize the container. All contained items will1020 // get serialized with depth zero.1021 if (HandleKnownContainerTypes(source, streamName, property, depth))1022 {1023 return;1024 }1025 1026 PSObject mshSource = PSObject.AsPSObject(source);1027 // If depth is zero, complex type should be serialized as string.1028 if (depth == 0 || SerializeAsString(mshSource))1029 {1030 HandlePSObjectAsString(mshSource, streamName, property, depth);1031 return;1032 }1033 1034 HandleComplexTypePSObject(source, streamName, property, depth);1035 return;1036 }1037 finally1038 {1039 _depthBelowTopLevel--;1040 Dbg.Assert(_depthBelowTopLevel >= 0, "depthBelowTopLevel should be >= 0");1041 }1042 }1043 1044 private bool HandleMaxDepth(object source, string streamName, string property)1045 {1046 if (_depthBelowTopLevel == MaxDepthBelowTopLevel)1047 {1048 // assert commented out because of clashes with Wei's tests1049 // Dbg.Assert(false, "We should never reach MaxDepthBelowTopLevel with non-malicious input");1050 1051 PSEtwLog.LogAnalyticError(PSEventId.Serializer_MaxDepthWhenSerializing, PSOpcode.Exception,1052 PSTask.Serialization, PSKeyword.Serializer, source.GetType().AssemblyQualifiedName, property, _depthBelowTopLevel);1053 1054 string content = Serialization.DeserializationTooDeep;1055 HandlePrimitiveKnownType(content, streamName, property);1056 return true;1057 }1058 else1059 {1060 return false;1061 }1062 }1063 1064 /// <summary>1065 /// Serializes Primitive Known Types.1066 /// </summary>1067 /// <returns>1068 /// true if source is handled, else false.1069 /// </returns>1070 private bool HandlePrimitiveKnownType1071 (1072 object source,1073 string streamName,1074 string property1075 )1076 {1077 Dbg.Assert(source != null, "caller should validate the parameter");1078 1079 // Check if source is of primitive known type1080 TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());1081 if (pktInfo != null)1082 {1083 WriteOnePrimitiveKnownType(this, streamName, property, source, pktInfo);1084 return true;1085 }1086 1087 return false;1088 }1089 1090 /// <summary>1091 /// Handles primitive known type by first converting it to a PSObject.In W8, extended1092 /// property data is stored external to PSObject. By converting to PSObject, we will1093 /// be able to retrieve and serialize the extended properties. This is tracked by1094 /// Win8: 414042.1095 /// </summary>1096 /// <param name="source"></param>1097 /// <param name="streamName"></param>1098 /// <param name="property"></param>1099 /// <param name="depth"></param>1100 /// <returns></returns>1101 private bool HandlePrimitiveKnownTypeByConvertingToPSObject(1102 object source,1103 string streamName,1104 string property,1105 int depth1106 )1107 {1108 Dbg.Assert(source != null, "caller should validate the parameter");1109 // Check if source is of primitive known type1110 TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());1111 if (pktInfo != null)1112 {1113 PSObject pktInfoPSObject = PSObject.AsPSObject(source);1114 return HandlePrimitiveKnownTypePSObject(pktInfoPSObject, streamName, property, depth);1115 }1116 1117 return false;1118 }1119 1120 /// <summary>1121 /// </summary>1122 /// <param name="source"></param>1123 /// <param name="streamName"></param>1124 /// <param name="property"></param>1125 /// <returns></returns>1126 private bool HandleSecureString(object source, string streamName, string property)1127 {1128 Dbg.Assert(source != null, "caller should validate the parameter");1129 1130 SecureString secureString = null;1131 secureString = source as SecureString;1132 PSObject moSource;1133 1134 if (secureString != null)1135 {1136 moSource = PSObject.AsPSObject(secureString);1137 }1138 else1139 {1140 moSource = source as PSObject;1141 }1142 1143 if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty)1144 {1145 // check if source is of type secure string1146 secureString = moSource.ImmediateBaseObject as SecureString;1147 if (secureString != null)1148 {1149 // the principle used in serialization is that serialization1150 // never throws, and if something can't be serialized nothing1151 // is written. So we write the elements only if encryption succeeds.1152 // However, in the case for non-Windows where secure string encryption1153 // is not yet supported, a PSCryptoException will be thrown.1154 string encryptedString;1155 if (_context.cryptoHelper != null)1156 {1157 encryptedString = _context.cryptoHelper.EncryptSecureString(secureString);1158 }1159 else1160 {1161 encryptedString = Microsoft.PowerShell.SecureStringHelper.Protect(secureString);1162 }1163 1164 if (property != null)1165 {1166 WriteStartElement(SerializationStrings.SecureStringTag);1167 WriteNameAttribute(property);1168 }1169 else1170 {1171 WriteStartElement(SerializationStrings.SecureStringTag);1172 }1173 1174 if (streamName != null)1175 {1176 WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);1177 }1178 1179 // Note: We do not use WriteRaw for serializing secure string. WriteString1180 // does necessary escaping which may be needed for certain1181 // characters.1182 _writer.WriteString(encryptedString);1183 1184 _writer.WriteEndElement();1185 1186 return true;1187 }1188 }1189 1190 return false;1191 }1192 1193 /// <summary>1194 /// Serializes PSObject whose base objects are of primitive known type.1195 /// </summary>1196 /// <param name="source"></param>1197 /// <param name="streamName"></param>1198 /// <param name="property"></param>1199 /// <param name="depth"></param>1200 /// <returns>