068f8206fe32dae0d91a4ba70430b71e45e9762e
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / NetDataContractSerializer.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Runtime.Serialization
6 {
7     using System;
8     using System.IO;
9     using System.Xml;
10     using System.Security;
11     using System.Collections;
12     using System.Security.Permissions;
13     using System.Runtime.CompilerServices;
14     using System.Runtime.Serialization.Formatters;
15     using System.Collections.Generic;
16     using System.Runtime.Serialization.Configuration;
17     using System.Reflection;
18
19     public sealed class NetDataContractSerializer : XmlObjectSerializer, IFormatter
20     {
21         XmlDictionaryString rootName;
22         XmlDictionaryString rootNamespace;
23         StreamingContext context;
24         SerializationBinder binder;
25         ISurrogateSelector surrogateSelector;
26         int maxItemsInObjectGraph;
27         bool ignoreExtensionDataObject;
28         FormatterAssemblyStyle assemblyFormat;
29         DataContract cachedDataContract;
30         static Hashtable typeNameCache = new Hashtable();
31
32         public NetDataContractSerializer()
33             : this(new StreamingContext(StreamingContextStates.All))
34         {
35         }
36
37         public NetDataContractSerializer(StreamingContext context)
38             : this(context, Int32.MaxValue, false, FormatterAssemblyStyle.Full, null)
39         {
40         }
41
42         public NetDataContractSerializer(StreamingContext context,
43             int maxItemsInObjectGraph,
44             bool ignoreExtensionDataObject,
45             FormatterAssemblyStyle assemblyFormat,
46             ISurrogateSelector surrogateSelector)
47         {
48             Initialize(context, maxItemsInObjectGraph, ignoreExtensionDataObject, assemblyFormat, surrogateSelector);
49         }
50
51         public NetDataContractSerializer(string rootName, string rootNamespace)
52             : this(rootName, rootNamespace, new StreamingContext(StreamingContextStates.All), Int32.MaxValue, false, FormatterAssemblyStyle.Full, null)
53         {
54         }
55
56         public NetDataContractSerializer(string rootName, string rootNamespace,
57             StreamingContext context,
58             int maxItemsInObjectGraph,
59             bool ignoreExtensionDataObject,
60             FormatterAssemblyStyle assemblyFormat,
61             ISurrogateSelector surrogateSelector)
62         {
63             XmlDictionary dictionary = new XmlDictionary(2);
64             Initialize(dictionary.Add(rootName), dictionary.Add(DataContract.GetNamespace(rootNamespace)), context, maxItemsInObjectGraph, ignoreExtensionDataObject, assemblyFormat, surrogateSelector);
65         }
66
67         public NetDataContractSerializer(XmlDictionaryString rootName, XmlDictionaryString rootNamespace)
68             : this(rootName, rootNamespace, new StreamingContext(StreamingContextStates.All), Int32.MaxValue, false, FormatterAssemblyStyle.Full, null)
69         {
70         }
71
72         public NetDataContractSerializer(XmlDictionaryString rootName, XmlDictionaryString rootNamespace,
73             StreamingContext context,
74             int maxItemsInObjectGraph,
75             bool ignoreExtensionDataObject,
76             FormatterAssemblyStyle assemblyFormat,
77             ISurrogateSelector surrogateSelector)
78         {
79             Initialize(rootName, rootNamespace, context, maxItemsInObjectGraph, ignoreExtensionDataObject, assemblyFormat, surrogateSelector);
80         }
81
82         void Initialize(StreamingContext context,
83             int maxItemsInObjectGraph,
84             bool ignoreExtensionDataObject,
85             FormatterAssemblyStyle assemblyFormat,
86             ISurrogateSelector surrogateSelector)
87         {
88             this.context = context;
89             if (maxItemsInObjectGraph < 0)
90                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxItemsInObjectGraph", SR.GetString(SR.ValueMustBeNonNegative)));
91             this.maxItemsInObjectGraph = maxItemsInObjectGraph;
92             this.ignoreExtensionDataObject = ignoreExtensionDataObject;
93             this.surrogateSelector = surrogateSelector;
94             this.AssemblyFormat = assemblyFormat;
95         }
96
97         void Initialize(XmlDictionaryString rootName, XmlDictionaryString rootNamespace,
98             StreamingContext context,
99             int maxItemsInObjectGraph,
100             bool ignoreExtensionDataObject,
101             FormatterAssemblyStyle assemblyFormat,
102             ISurrogateSelector surrogateSelector)
103         {
104             Initialize(context, maxItemsInObjectGraph, ignoreExtensionDataObject, assemblyFormat, surrogateSelector);
105             this.rootName = rootName;
106             this.rootNamespace = rootNamespace;
107         }
108
109         static bool? unsafeTypeForwardingEnabled;
110         internal static bool UnsafeTypeForwardingEnabled
111         {
112             [Fx.Tag.SecurityNote(Critical = "Calls Security Critical method NetDataContractSerializerSection.TryUnsafeGetSection.", Safe = "The ConfigSection instance is not leaked.")]
113             [SecuritySafeCritical]
114             get
115             {
116                 if (unsafeTypeForwardingEnabled == null)
117                 {
118                     NetDataContractSerializerSection section;
119                     if (NetDataContractSerializerSection.TryUnsafeGetSection(out section))
120                     {
121                         unsafeTypeForwardingEnabled = section.EnableUnsafeTypeForwarding;
122                     }
123                     else
124                     {
125                         unsafeTypeForwardingEnabled = false;
126                     }
127                 }
128                 Fx.Assert(unsafeTypeForwardingEnabled != null, "unsafeTypeForwardingEnabled should not be null.");
129                 return unsafeTypeForwardingEnabled.Value;
130             }
131         }
132
133         public StreamingContext Context
134         {
135             get { return context; }
136             set { context = value; }
137         }
138
139         public SerializationBinder Binder
140         {
141             get { return binder; }
142             set { binder = value; }
143         }
144
145         public ISurrogateSelector SurrogateSelector
146         {
147             get { return surrogateSelector; }
148             set { surrogateSelector = value; }
149         }
150
151         public FormatterAssemblyStyle AssemblyFormat
152         {
153             get { return assemblyFormat; }
154             set
155             {
156                 if (value != FormatterAssemblyStyle.Full && value != FormatterAssemblyStyle.Simple)
157                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.InvalidAssemblyFormat, value)));
158                 assemblyFormat = value;
159             }
160         }
161
162         public int MaxItemsInObjectGraph
163         {
164             get { return maxItemsInObjectGraph; }
165         }
166
167         public bool IgnoreExtensionDataObject
168         {
169             get { return ignoreExtensionDataObject; }
170         }
171
172         public void Serialize(Stream stream, object graph)
173         {
174             base.WriteObject(stream, graph);
175         }
176
177         public object Deserialize(Stream stream)
178         {
179             return base.ReadObject(stream);
180         }
181
182         internal override void InternalWriteObject(XmlWriterDelegator writer, object graph)
183         {
184             Hashtable surrogateDataContracts = null;
185             DataContract contract = GetDataContract(graph, ref surrogateDataContracts);
186
187             InternalWriteStartObject(writer, graph, contract);
188             InternalWriteObjectContent(writer, graph, contract, surrogateDataContracts);
189             InternalWriteEndObject(writer);
190         }
191
192         public override void WriteObject(XmlWriter writer, object graph)
193         {
194             WriteObjectHandleExceptions(new XmlWriterDelegator(writer), graph);
195         }
196
197         public override void WriteStartObject(XmlWriter writer, object graph)
198         {
199             WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph);
200         }
201
202         public override void WriteObjectContent(XmlWriter writer, object graph)
203         {
204             WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph);
205         }
206
207         public override void WriteEndObject(XmlWriter writer)
208         {
209             WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer));
210         }
211
212         public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
213         {
214             WriteStartObjectHandleExceptions(new XmlWriterDelegator(writer), graph);
215         }
216
217         internal override void InternalWriteStartObject(XmlWriterDelegator writer, object graph)
218         {
219             Hashtable surrogateDataContracts = null;
220             DataContract contract = GetDataContract(graph, ref surrogateDataContracts);
221             InternalWriteStartObject(writer, graph, contract);
222         }
223
224         void InternalWriteStartObject(XmlWriterDelegator writer, object graph, DataContract contract)
225         {
226             WriteRootElement(writer, contract, rootName, rootNamespace, CheckIfNeedsContractNsAtRoot(rootName, rootNamespace, contract));
227         }
228
229         public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
230         {
231             WriteObjectContentHandleExceptions(new XmlWriterDelegator(writer), graph);
232         }
233
234         internal override void InternalWriteObjectContent(XmlWriterDelegator writer, object graph)
235         {
236             Hashtable surrogateDataContracts = null;
237             DataContract contract = GetDataContract(graph, ref surrogateDataContracts);
238             InternalWriteObjectContent(writer, graph, contract, surrogateDataContracts);
239         }
240
241         void InternalWriteObjectContent(XmlWriterDelegator writer, object graph, DataContract contract, Hashtable surrogateDataContracts)
242         {
243             if (MaxItemsInObjectGraph == 0)
244                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ExceededMaxItemsQuota, MaxItemsInObjectGraph)));
245
246             if (IsRootXmlAny(rootName, contract))
247             {
248                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.IsAnyNotSupportedByNetDataContractSerializer, contract.UnderlyingType)));
249             }
250             else if (graph == null)
251             {
252                 WriteNull(writer);
253             }
254             else
255             {
256                 Type graphType = graph.GetType();
257                 if (contract.UnderlyingType != graphType)
258                     contract = GetDataContract(graph, ref surrogateDataContracts);
259
260                 XmlObjectSerializerWriteContext context = null;
261                 if (contract.CanContainReferences)
262                 {
263                     context = XmlObjectSerializerWriteContext.CreateContext(this, surrogateDataContracts);
264                     context.HandleGraphAtTopLevel(writer, graph, contract);
265                 }
266
267                 WriteClrTypeInfo(writer, contract, binder);
268                 contract.WriteXmlValue(writer, graph, context);
269             }
270         }
271
272         // Update the overloads whenever you are changing this method
273         internal static void WriteClrTypeInfo(XmlWriterDelegator writer, DataContract dataContract, SerializationBinder binder)
274         {
275             if (!dataContract.IsISerializable && !(dataContract is SurrogateDataContract))
276             {
277                 TypeInformation typeInformation = null;
278                 Type clrType = dataContract.OriginalUnderlyingType;
279                 string clrTypeName = null;
280                 string clrAssemblyName = null;
281
282                 if (binder != null)
283                 {
284                     binder.BindToName(clrType, out clrAssemblyName, out clrTypeName);
285                 }
286
287                 if (clrTypeName == null)
288                 {
289                     typeInformation = NetDataContractSerializer.GetTypeInformation(clrType);
290                     clrTypeName = typeInformation.FullTypeName;
291                 }
292
293                 if (clrAssemblyName == null)
294                 {
295                     clrAssemblyName = (typeInformation == null) ?
296                         NetDataContractSerializer.GetTypeInformation(clrType).AssemblyString :
297                         typeInformation.AssemblyString;
298
299                     // Throw in the [TypeForwardedFrom] case to prevent a partially trusted assembly from forwarding itself to an assembly with higher privileges
300                     if (!UnsafeTypeForwardingEnabled && !clrType.Assembly.IsFullyTrusted && !IsAssemblyNameForwardingSafe(clrType.Assembly.FullName, clrAssemblyName))
301                     {
302                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.TypeCannotBeForwardedFrom, DataContract.GetClrTypeFullName(clrType), clrType.Assembly.FullName, clrAssemblyName)));
303                     }
304                 }
305
306                 WriteClrTypeInfo(writer, clrTypeName, clrAssemblyName);
307             }
308         }
309
310         // Update the overloads whenever you are changing this method
311         internal static void WriteClrTypeInfo(XmlWriterDelegator writer, Type dataContractType, SerializationBinder binder, string defaultClrTypeName, string defaultClrAssemblyName)
312         {
313             string clrTypeName = null;
314             string clrAssemblyName = null;
315
316             if (binder != null)
317             {
318                 binder.BindToName(dataContractType, out clrAssemblyName, out clrTypeName);
319             }
320
321             if (clrTypeName == null)
322             {
323                 clrTypeName = defaultClrTypeName;
324             }
325
326             if (clrAssemblyName == null)
327             {
328                 clrAssemblyName = defaultClrAssemblyName;
329             }
330
331             WriteClrTypeInfo(writer, clrTypeName, clrAssemblyName);
332         }
333
334         // Update the overloads whenever you are changing this method
335         internal static void WriteClrTypeInfo(XmlWriterDelegator writer, Type dataContractType, SerializationBinder binder, SerializationInfo serInfo)
336         {
337             TypeInformation typeInformation = null;
338             string clrTypeName = null;
339             string clrAssemblyName = null;
340
341             if (binder != null)
342             {
343                 binder.BindToName(dataContractType, out clrAssemblyName, out clrTypeName);
344             }
345
346             if (clrTypeName == null)
347             {
348                 if (serInfo.IsFullTypeNameSetExplicit)
349                 {
350                     clrTypeName = serInfo.FullTypeName;
351                 }
352                 else
353                 {
354                     typeInformation = NetDataContractSerializer.GetTypeInformation(serInfo.ObjectType);
355                     clrTypeName = typeInformation.FullTypeName;
356                 }
357             }
358
359             if (clrAssemblyName == null)
360             {
361                 if (serInfo.IsAssemblyNameSetExplicit)
362                 {
363                     clrAssemblyName = serInfo.AssemblyName;
364                 }
365                 else
366                 {
367                     clrAssemblyName = (typeInformation == null) ?
368                     NetDataContractSerializer.GetTypeInformation(serInfo.ObjectType).AssemblyString :
369                     typeInformation.AssemblyString;
370                 }
371             }
372
373             WriteClrTypeInfo(writer, clrTypeName, clrAssemblyName);
374         }
375
376         static void WriteClrTypeInfo(XmlWriterDelegator writer, string clrTypeName, string clrAssemblyName)
377         {
378             if (clrTypeName != null)
379                 writer.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.ClrTypeLocalName, DictionaryGlobals.SerializationNamespace, DataContract.GetClrTypeString(clrTypeName));
380             if (clrAssemblyName != null)
381                 writer.WriteAttributeString(Globals.SerPrefix, DictionaryGlobals.ClrAssemblyLocalName, DictionaryGlobals.SerializationNamespace, DataContract.GetClrTypeString(clrAssemblyName));
382         }
383
384         public override void WriteEndObject(XmlDictionaryWriter writer)
385         {
386             WriteEndObjectHandleExceptions(new XmlWriterDelegator(writer));
387         }
388
389         internal override void InternalWriteEndObject(XmlWriterDelegator writer)
390         {
391             writer.WriteEndElement();
392         }
393
394         public override object ReadObject(XmlReader reader)
395         {
396             return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), true /*verifyObjectName*/);
397         }
398
399         public override object ReadObject(XmlReader reader, bool verifyObjectName)
400         {
401             return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName);
402         }
403
404         public override bool IsStartObject(XmlReader reader)
405         {
406             return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader));
407         }
408
409         public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
410         {
411             return ReadObjectHandleExceptions(new XmlReaderDelegator(reader), verifyObjectName);
412         }
413
414         public override bool IsStartObject(XmlDictionaryReader reader)
415         {
416             return IsStartObjectHandleExceptions(new XmlReaderDelegator(reader));
417         }
418
419         internal override object InternalReadObject(XmlReaderDelegator xmlReader, bool verifyObjectName)
420         {
421             if (MaxItemsInObjectGraph == 0)
422                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ExceededMaxItemsQuota, MaxItemsInObjectGraph)));
423
424             // verifyObjectName has no effect in SharedType mode
425             if (!IsStartElement(xmlReader))
426             {
427                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationExceptionWithReaderDetails(SR.GetString(SR.ExpectingElementAtDeserialize, XmlNodeType.Element), xmlReader));
428             }
429
430             XmlObjectSerializerReadContext context = XmlObjectSerializerReadContext.CreateContext(this);
431             return context.InternalDeserialize(xmlReader, null, null, null);
432         }
433
434         internal override bool InternalIsStartObject(XmlReaderDelegator reader)
435         {
436             return IsStartElement(reader);
437         }
438
439         internal DataContract GetDataContract(object obj, ref Hashtable surrogateDataContracts)
440         {
441             return GetDataContract(((obj == null) ? Globals.TypeOfObject : obj.GetType()), ref surrogateDataContracts);
442         }
443
444         internal DataContract GetDataContract(Type type, ref Hashtable surrogateDataContracts)
445         {
446             return GetDataContract(type.TypeHandle, type, ref surrogateDataContracts);
447         }
448
449         internal DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type, ref Hashtable surrogateDataContracts)
450         {
451             DataContract dataContract = GetDataContractFromSurrogateSelector(surrogateSelector, Context, typeHandle, type, ref surrogateDataContracts);
452             if (dataContract != null)
453                 return dataContract;
454
455             if (cachedDataContract == null)
456             {
457                 dataContract = DataContract.GetDataContract(typeHandle, type, SerializationMode.SharedType);
458                 cachedDataContract = dataContract;
459                 return dataContract;
460             }
461
462             DataContract currentCachedDataContract = cachedDataContract;
463             if (currentCachedDataContract.UnderlyingType.TypeHandle.Equals(typeHandle))
464                 return currentCachedDataContract;
465
466             return DataContract.GetDataContract(typeHandle, type, SerializationMode.SharedType);
467         }
468
469         [Fx.Tag.SecurityNote(Critical = "Calls the critical methods of ISurrogateSelector", Safe = "Demands for FullTrust")]
470         [SecuritySafeCritical]
471         [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
472         [MethodImpl(MethodImplOptions.NoInlining)]
473         static ISerializationSurrogate GetSurrogate(Type type, ISurrogateSelector surrogateSelector, StreamingContext context)
474         {
475             ISurrogateSelector surrogateSelectorNotUsed;
476             return surrogateSelector.GetSurrogate(type, context, out surrogateSelectorNotUsed);
477         }
478
479         internal static DataContract GetDataContractFromSurrogateSelector(ISurrogateSelector surrogateSelector, StreamingContext context, RuntimeTypeHandle typeHandle, Type type, ref Hashtable surrogateDataContracts)
480         {
481             if (surrogateSelector == null)
482                 return null;
483
484             if (type == null)
485                 type = Type.GetTypeFromHandle(typeHandle);
486             DataContract builtInDataContract = DataContract.GetBuiltInDataContract(type);
487             if (builtInDataContract != null)
488                 return builtInDataContract;
489             if (surrogateDataContracts != null)
490             {
491                 DataContract cachedSurrogateContract = (DataContract)surrogateDataContracts[type];
492                 if (cachedSurrogateContract != null)
493                     return cachedSurrogateContract;
494             }
495             DataContract surrogateContract = null;
496             ISerializationSurrogate surrogate = GetSurrogate(type, surrogateSelector, context);
497             if (surrogate != null)
498                 surrogateContract = new SurrogateDataContract(type, surrogate);
499             else if (type.IsArray)
500             {
501                 Type elementType = type.GetElementType();
502                 DataContract itemContract = GetDataContractFromSurrogateSelector(surrogateSelector, context, elementType.TypeHandle, elementType, ref surrogateDataContracts);
503                 if (itemContract == null)
504                     itemContract = DataContract.GetDataContract(elementType.TypeHandle, elementType, SerializationMode.SharedType);
505                 surrogateContract = new CollectionDataContract(type, itemContract);
506             }
507             if (surrogateContract != null)
508             {
509                 if (surrogateDataContracts == null)
510                     surrogateDataContracts = new Hashtable();
511                 surrogateDataContracts.Add(type, surrogateContract);
512                 return surrogateContract;
513             }
514             return null;
515         }
516
517
518         internal static TypeInformation GetTypeInformation(Type type)
519         {
520             TypeInformation typeInformation = null;
521             object typeInformationObject = typeNameCache[type];
522             if (typeInformationObject == null)
523             {
524                 bool hasTypeForwardedFrom;
525                 string assemblyName = DataContract.GetClrAssemblyName(type, out hasTypeForwardedFrom);
526                 typeInformation = new TypeInformation(DataContract.GetClrTypeFullNameUsingTypeForwardedFromAttribute(type), assemblyName, hasTypeForwardedFrom);
527                 lock (typeNameCache)
528                 {
529                     typeNameCache[type] = typeInformation;
530                 }
531             }
532             else
533             {
534                 typeInformation = (TypeInformation)typeInformationObject;
535             }
536             return typeInformation;
537         }
538
539         static bool IsAssemblyNameForwardingSafe(string originalAssemblyName, string newAssemblyName)
540         {
541             if (originalAssemblyName == newAssemblyName)
542             {
543                 return true;
544             }
545
546             AssemblyName originalAssembly = new AssemblyName(originalAssemblyName);
547             AssemblyName newAssembly = new AssemblyName(newAssemblyName);
548
549             // mscorlib will get loaded by the runtime regardless of its string casing or its public key token,
550             // so setting the assembly name to mscorlib is always unsafe
551             if (string.Equals(newAssembly.Name, Globals.MscorlibAssemblySimpleName, StringComparison.OrdinalIgnoreCase) ||
552                 string.Equals(newAssembly.Name, Globals.MscorlibFileName, StringComparison.OrdinalIgnoreCase))
553             {
554                 return false;
555             }
556
557             return IsPublicKeyTokenForwardingSafe(originalAssembly.GetPublicKeyToken(), newAssembly.GetPublicKeyToken());
558         }
559
560         static bool IsPublicKeyTokenForwardingSafe(byte[] sourceToken, byte[] destinationToken)
561         {
562             if (sourceToken == null || destinationToken == null || sourceToken.Length == 0 || destinationToken.Length == 0 || sourceToken.Length != destinationToken.Length)
563             {
564                 return false;
565             }
566             
567             for (int i = 0; i < sourceToken.Length; i++)
568             {
569                 if (sourceToken[i] != destinationToken[i])
570                 {
571                     return false;
572                 }
573             }
574             return true;
575         }
576     }
577
578 }