disable Win32 registry dependencies in XmlReaderSettings on MOBILE builds.
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / XmlReaderSettings.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlReaderSettings.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
7
8 using System.IO;
9 using System.Diagnostics;
10 using System.Security.Permissions;
11 #if !SILVERLIGHT
12 using Microsoft.Win32;
13 using System.Globalization;
14 using System.Security;
15 using System.Xml.Schema;
16 using System.Xml.XmlConfiguration;
17 #endif
18 using System.Runtime.Versioning;
19
20 namespace System.Xml {
21
22     // XmlReaderSettings class specifies basic features of an XmlReader.
23 #if !SILVERLIGHT && !MOBILE
24     [PermissionSetAttribute(SecurityAction.InheritanceDemand, Name = "FullTrust")]
25 #endif
26     public sealed class XmlReaderSettings {
27
28         //
29         // Fields
30         //
31
32 #if ASYNC || FEATURE_NETCORE
33         bool useAsync;
34 #endif
35
36
37         // Nametable
38         XmlNameTable nameTable;
39
40         // XmlResolver
41         XmlResolver xmlResolver;
42
43         // Text settings
44         int lineNumberOffset;
45         int linePositionOffset;
46
47         // Conformance settings
48         ConformanceLevel conformanceLevel;
49         bool checkCharacters;
50
51         long maxCharactersInDocument;
52         long maxCharactersFromEntities;
53
54         // Filtering settings
55         bool ignoreWhitespace;
56         bool ignorePIs;
57         bool ignoreComments;
58
59         // security settings
60         DtdProcessing dtdProcessing;
61
62 #if !SILVERLIGHT
63         //Validation settings
64         ValidationType validationType;
65         XmlSchemaValidationFlags validationFlags;
66         XmlSchemaSet schemas;
67         ValidationEventHandler valEventHandler;
68 #endif
69
70         // other settings
71         bool closeInput;
72
73         // read-only flag
74         bool isReadOnly;
75
76         //
77         // Constructor
78         //
79         public XmlReaderSettings() {
80             Initialize();
81         }
82
83 #if !FEATURE_LEGACYNETCF
84         // introduced for supporting design-time loading of phone assemblies
85         [Obsolete("This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.", true)]
86         [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
87 #endif
88         public XmlReaderSettings(XmlResolver resolver) {
89             Initialize(resolver);
90         }
91
92         //
93         // Properties
94         //
95
96 #if ASYNC || FEATURE_NETCORE
97         public bool Async {
98             get {
99                 return useAsync;
100             }
101             set {
102                 CheckReadOnly("Async");
103                 useAsync = value;
104             }
105         }
106 #endif
107
108         // Nametable
109         public XmlNameTable NameTable {
110             get {
111                 return nameTable;
112             }
113             set {
114                 CheckReadOnly("NameTable");
115                 nameTable = value;
116             }
117         }
118
119 #if !SILVERLIGHT
120         // XmlResolver
121         internal bool IsXmlResolverSet {
122             get;
123             set; // keep set internal as we need to call it from the schema validation code
124         }
125 #endif
126
127         public XmlResolver XmlResolver {
128             set {
129                 CheckReadOnly("XmlResolver");
130                 xmlResolver = value;
131 #if !SILVERLIGHT
132                 IsXmlResolverSet = true;
133 #endif
134             }
135         }
136
137         internal XmlResolver GetXmlResolver() {
138             return xmlResolver;
139         }
140
141 #if !SILVERLIGHT && !MOBILE
142         //This is used by get XmlResolver in Xsd.
143         //Check if the config set to prohibit default resovler
144         //notice we must keep GetXmlResolver() to avoid dead lock when init System.Config.ConfigurationManager
145         internal XmlResolver GetXmlResolver_CheckConfig() { 
146             if (System.Xml.XmlConfiguration.XmlReaderSection.ProhibitDefaultUrlResolver && !IsXmlResolverSet)
147                 return null;
148             else
149                 return xmlResolver;
150         }
151 #endif
152
153         // Text settings
154         public int LineNumberOffset {
155             get {
156                 return lineNumberOffset;
157             }
158             set {
159                 CheckReadOnly("LineNumberOffset");
160                 lineNumberOffset = value;
161             }
162         }
163
164         public int LinePositionOffset {
165             get {
166                 return linePositionOffset;
167             }
168             set {
169                 CheckReadOnly("LinePositionOffset");
170                 linePositionOffset = value;
171             }
172         }
173
174         // Conformance settings
175         public ConformanceLevel ConformanceLevel {
176             get {
177                 return conformanceLevel;
178             }
179             set {
180                 CheckReadOnly("ConformanceLevel");
181
182                 if ((uint)value > (uint)ConformanceLevel.Document) {
183                     throw new ArgumentOutOfRangeException("value");
184                 }
185                 conformanceLevel = value;
186             }
187         }
188
189         public bool CheckCharacters {
190             get {
191                 return checkCharacters;
192             }
193             set {
194                 CheckReadOnly("CheckCharacters");
195                 checkCharacters = value;
196             }
197         }
198
199         public long MaxCharactersInDocument {
200             get {
201                 return maxCharactersInDocument;
202             }
203             set {
204                 CheckReadOnly("MaxCharactersInDocument");
205                 if (value < 0) {
206                     throw new ArgumentOutOfRangeException("value");
207                 }
208                 maxCharactersInDocument = value;
209             }
210         }
211
212         public long MaxCharactersFromEntities {
213             get {
214                 return maxCharactersFromEntities;
215             }
216             set {
217                 CheckReadOnly("MaxCharactersFromEntities");
218                 if (value < 0) {
219                     throw new ArgumentOutOfRangeException("value");
220                 }
221                 maxCharactersFromEntities = value;
222             }
223         }
224
225         // Filtering settings
226         public bool IgnoreWhitespace {
227             get {
228                 return ignoreWhitespace;
229             }
230             set {
231                 CheckReadOnly("IgnoreWhitespace");
232                 ignoreWhitespace = value;
233             }
234         }
235
236         public bool IgnoreProcessingInstructions {
237             get {
238                 return ignorePIs;
239             }
240             set {
241                 CheckReadOnly("IgnoreProcessingInstructions");
242                 ignorePIs = value;
243             }
244         }
245
246         public bool IgnoreComments {
247             get {
248                 return ignoreComments;
249             }
250             set {
251                 CheckReadOnly("IgnoreComments");
252                 ignoreComments = value;
253             }
254         }
255
256 #if !SILVERLIGHT
257         [Obsolete("Use XmlReaderSettings.DtdProcessing property instead.")]
258         public bool ProhibitDtd {
259             get {
260                 return dtdProcessing == DtdProcessing.Prohibit;
261             }
262             set {
263                 CheckReadOnly("ProhibitDtd");
264                 dtdProcessing = value ? DtdProcessing.Prohibit : DtdProcessing.Parse;
265             }
266         }
267 #endif
268
269         public DtdProcessing DtdProcessing {
270             get {
271                 return dtdProcessing;
272             }
273             set {
274                 CheckReadOnly("DtdProcessing");
275
276                 if ((uint)value > (uint)DtdProcessing.Parse) {
277                     throw new ArgumentOutOfRangeException("value");
278                 }
279                 dtdProcessing = value;
280             }
281         }
282
283         public bool CloseInput {
284             get {
285                 return closeInput;
286             }
287             set {
288                 CheckReadOnly("CloseInput");
289                 closeInput = value;
290             }
291         }
292
293 #if !SILVERLIGHT
294         public ValidationType ValidationType {
295             get {
296                 return validationType;
297             }
298             set {
299                 CheckReadOnly("ValidationType");
300
301                 if ((uint)value > (uint)ValidationType.Schema) {
302                     throw new ArgumentOutOfRangeException("value");
303                 }
304                 validationType = value;
305             }
306         }
307
308         public XmlSchemaValidationFlags ValidationFlags {
309             get {
310                 return validationFlags;
311             }
312             set {
313                 CheckReadOnly("ValidationFlags");
314
315                 if ((uint)value > (uint)(XmlSchemaValidationFlags.ProcessInlineSchema | XmlSchemaValidationFlags.ProcessSchemaLocation |
316                                            XmlSchemaValidationFlags.ReportValidationWarnings | XmlSchemaValidationFlags.ProcessIdentityConstraints |
317                                            XmlSchemaValidationFlags.AllowXmlAttributes)) {
318                     throw new ArgumentOutOfRangeException("value");
319                 }
320                 validationFlags = value;
321             }
322         }
323
324         public XmlSchemaSet Schemas {
325             get {
326                 if (schemas == null) {
327                     schemas = new XmlSchemaSet();
328                 }
329                 return schemas;
330             }
331             set {
332                 CheckReadOnly("Schemas");
333                 schemas = value;
334             }
335         }
336
337         public event ValidationEventHandler ValidationEventHandler {
338             add {
339                 CheckReadOnly("ValidationEventHandler");
340                 valEventHandler += value;
341             }
342             remove {
343                 CheckReadOnly("ValidationEventHandler");
344                 valEventHandler -= value;
345             }
346         }
347 #endif
348
349         //
350         // Public methods
351         //
352         public void Reset() {
353             CheckReadOnly("Reset");
354             Initialize();
355         }
356
357         public XmlReaderSettings Clone() {
358             XmlReaderSettings clonedSettings = this.MemberwiseClone() as XmlReaderSettings;
359             clonedSettings.ReadOnly = false;
360             return clonedSettings;
361         }
362
363         //
364         // Internal methods
365         //
366 #if !SILVERLIGHT
367         internal ValidationEventHandler GetEventHandler() {
368             return valEventHandler;
369         }
370 #endif
371
372
373 #if !SILVERLIGHT
374         [ResourceConsumption(ResourceScope.Machine)]
375         [ResourceExposure(ResourceScope.Machine)]
376 #endif
377         internal XmlReader CreateReader(String inputUri, XmlParserContext inputContext) {
378             if (inputUri == null) {
379                 throw new ArgumentNullException("inputUri");
380             }
381             if (inputUri.Length == 0) {
382                 throw new ArgumentException(Res.GetString(Res.XmlConvert_BadUri), "inputUri");
383             }
384
385             // resolve and open the url
386             XmlResolver tmpResolver = this.GetXmlResolver();
387             if (tmpResolver == null) {
388                 tmpResolver = CreateDefaultResolver();
389             }
390
391             // create text XML reader
392             XmlReader reader = new XmlTextReaderImpl(inputUri, this, inputContext, tmpResolver);
393
394 #if !SILVERLIGHT
395             // wrap with validating reader
396             if (this.ValidationType != ValidationType.None) {
397                 reader = AddValidation(reader);
398             }
399 #endif
400
401 #if ASYNC
402             if (useAsync) {
403                 reader = XmlAsyncCheckReader.CreateAsyncCheckWrapper(reader);
404             }
405 #endif
406             return reader;
407         }
408
409         internal XmlReader CreateReader(Stream input, Uri baseUri, string baseUriString, XmlParserContext inputContext) {
410             if (input == null) {
411                 throw new ArgumentNullException("input");
412             }
413             if (baseUriString == null) {
414                 if (baseUri == null) {
415                     baseUriString = string.Empty;
416                 }
417                 else {
418                     baseUriString = baseUri.ToString();
419                 }
420             }
421
422             // create text XML reader
423             XmlReader reader = new XmlTextReaderImpl(input, null, 0, this, baseUri, baseUriString, inputContext, closeInput);
424
425 #if !SILVERLIGHT
426             // wrap with validating reader
427             if (this.ValidationType != ValidationType.None) {
428                 reader = AddValidation(reader);
429             }
430 #endif
431
432 #if ASYNC
433             if (useAsync) {
434                 reader = XmlAsyncCheckReader.CreateAsyncCheckWrapper(reader);
435             }
436 #endif
437
438             return reader;
439         }
440
441         internal XmlReader CreateReader(TextReader input, string baseUriString, XmlParserContext inputContext) {
442             if (input == null) {
443                 throw new ArgumentNullException("input");
444             }
445             if (baseUriString == null) {
446                 baseUriString = string.Empty;
447             }
448
449             // create xml text reader
450             XmlReader reader = new XmlTextReaderImpl(input, this, baseUriString, inputContext);
451
452 #if !SILVERLIGHT
453             // wrap with validating reader
454             if (this.ValidationType != ValidationType.None) {
455                 reader = AddValidation(reader);
456             }
457 #endif
458
459 #if ASYNC
460             if (useAsync) {
461                 reader = XmlAsyncCheckReader.CreateAsyncCheckWrapper(reader);
462             }
463 #endif
464
465             return reader;
466         }
467
468         internal XmlReader CreateReader(XmlReader reader) {
469             if (reader == null) {
470                 throw new ArgumentNullException("reader");
471             }
472 #if !ASYNC || SILVERLIGHT || FEATURE_NETCORE
473             // wrap with conformance layer (if needed)
474             return AddConformanceWrapper(reader);
475 #else
476             return AddValidationAndConformanceWrapper(reader);
477 #endif // !ASYNC || SILVERLIGHT || FEATURE_NETCORE
478         }
479
480         internal bool ReadOnly {
481             get {
482                 return isReadOnly;
483             }
484             set {
485                 isReadOnly = value;
486             }
487         }
488
489         void CheckReadOnly(string propertyName) {
490             if (isReadOnly) {
491                 throw new XmlException(Res.Xml_ReadOnlyProperty, this.GetType().Name + '.' + propertyName);
492             }
493         }
494
495         //
496         // Private methods
497         //
498         void Initialize() {
499             Initialize(null);
500         }
501
502         void Initialize(XmlResolver resolver) {
503             nameTable = null;
504 #if !SILVERLIGHT && !MOBILE
505             if (!EnableLegacyXmlSettings())
506             {
507                 xmlResolver = resolver;
508                 // limit the entity resolving to 10 million character. the caller can still
509                 // override it to any other value or set it to zero for unlimiting it
510                 maxCharactersFromEntities = (long) 1e7;
511             }
512             else
513 #endif            
514             {
515                 xmlResolver = (resolver == null ? CreateDefaultResolver() : resolver);
516                 maxCharactersFromEntities = 0;
517             }
518             lineNumberOffset = 0;
519             linePositionOffset = 0;
520             checkCharacters = true;
521             conformanceLevel = ConformanceLevel.Document;
522
523             ignoreWhitespace = false;
524             ignorePIs = false;
525             ignoreComments = false;
526             dtdProcessing = DtdProcessing.Prohibit;
527             closeInput = false;
528
529             maxCharactersInDocument = 0;
530
531 #if !SILVERLIGHT
532             schemas = null;
533             validationType = ValidationType.None;
534             validationFlags = XmlSchemaValidationFlags.ProcessIdentityConstraints;
535             validationFlags |= XmlSchemaValidationFlags.AllowXmlAttributes;
536 #endif
537
538 #if ASYNC || FEATURE_NETCORE
539             useAsync = false;
540 #endif
541
542             isReadOnly = false;
543 #if !SILVERLIGHT
544             IsXmlResolverSet = false;
545 #endif
546         }
547
548         static XmlResolver CreateDefaultResolver() {
549 #if SILVERLIGHT
550             return new XmlXapResolver();
551 #else
552             return new XmlUrlResolver();
553 #endif
554         }
555
556 #if !SILVERLIGHT
557         internal XmlReader AddValidation(XmlReader reader) {
558             if (this.validationType == ValidationType.Schema) {
559                 XmlResolver resolver = GetXmlResolver_CheckConfig();
560
561                 if (resolver == null &&
562                     !this.IsXmlResolverSet &&
563                     !EnableLegacyXmlSettings())
564                 {
565                     resolver = new XmlUrlResolver();
566                 }
567                 reader = new XsdValidatingReader(reader, resolver, this);
568             }
569             else if (this.validationType == ValidationType.DTD) {
570                 reader = CreateDtdValidatingReader(reader);
571             }
572             return reader;
573         }
574
575         private XmlReader AddValidationAndConformanceWrapper(XmlReader reader) {
576             // wrap with DTD validating reader
577             if (this.validationType == ValidationType.DTD) {
578                 reader = CreateDtdValidatingReader(reader);
579             }
580             // add conformance checking (must go after DTD validation because XmlValidatingReader works only on XmlTextReader),
581             // but before XSD validation because of typed value access
582             reader = AddConformanceWrapper(reader);
583
584             if (this.validationType == ValidationType.Schema) {
585                 reader = new XsdValidatingReader(reader, GetXmlResolver_CheckConfig(), this);
586             }
587             return reader;
588         }
589
590         private XmlValidatingReaderImpl CreateDtdValidatingReader(XmlReader baseReader) {
591             return new XmlValidatingReaderImpl(baseReader, this.GetEventHandler(), (this.ValidationFlags & XmlSchemaValidationFlags.ProcessIdentityConstraints) != 0);
592         }
593 #endif // !SILVERLIGHT
594
595         internal XmlReader AddConformanceWrapper(XmlReader baseReader) {
596             XmlReaderSettings baseReaderSettings = baseReader.Settings;
597             bool checkChars = false;
598             bool noWhitespace = false;
599             bool noComments = false;
600             bool noPIs = false;
601             DtdProcessing dtdProc = (DtdProcessing)(-1);
602             bool needWrap = false;
603
604             if (baseReaderSettings == null) {
605
606 #pragma warning disable 618
607
608 #if SILVERLIGHT
609                 if (this.conformanceLevel != ConformanceLevel.Auto) {
610                     throw new InvalidOperationException(Res.GetString(Res.Xml_IncompatibleConformanceLevel, this.conformanceLevel.ToString()));
611                 }
612 #else
613                 if (this.conformanceLevel != ConformanceLevel.Auto && this.conformanceLevel != XmlReader.GetV1ConformanceLevel(baseReader)) {
614                     throw new InvalidOperationException(Res.GetString(Res.Xml_IncompatibleConformanceLevel, this.conformanceLevel.ToString()));
615                 }
616 #endif
617
618 #if !SILVERLIGHT
619                 // get the V1 XmlTextReader ref
620                 XmlTextReader v1XmlTextReader = baseReader as XmlTextReader;
621                 if (v1XmlTextReader == null) {
622                     XmlValidatingReader vr = baseReader as XmlValidatingReader;
623                     if (vr != null) {
624                         v1XmlTextReader = (XmlTextReader)vr.Reader;
625                     }
626                 }
627 #endif
628
629                 // assume the V1 readers already do all conformance checking; 
630                 // wrap only if IgnoreWhitespace, IgnoreComments, IgnoreProcessingInstructions or ProhibitDtd is true;
631                 if (this.ignoreWhitespace) {
632                     WhitespaceHandling wh = WhitespaceHandling.All;
633 #if !SILVERLIGHT
634                     // special-case our V1 readers to see if whey already filter whitespaces
635                     if (v1XmlTextReader != null) {
636                         wh = v1XmlTextReader.WhitespaceHandling;
637                     }
638 #endif
639                     if (wh == WhitespaceHandling.All) {
640                         noWhitespace = true;
641                         needWrap = true;
642                     }
643                 }
644                 if (this.ignoreComments) {
645                     noComments = true;
646                     needWrap = true;
647                 }
648                 if (this.ignorePIs) {
649                     noPIs = true;
650                     needWrap = true;
651                 }
652                 // DTD processing
653                 DtdProcessing baseDtdProcessing = DtdProcessing.Parse;
654 #if !SILVERLIGHT
655                 if (v1XmlTextReader != null) {
656                     baseDtdProcessing = v1XmlTextReader.DtdProcessing;
657                 }
658 #endif
659                 if ((this.dtdProcessing == DtdProcessing.Prohibit && baseDtdProcessing != DtdProcessing.Prohibit) ||
660                     (this.dtdProcessing == DtdProcessing.Ignore && baseDtdProcessing == DtdProcessing.Parse)) {
661                     dtdProc = this.dtdProcessing;
662                     needWrap = true;
663                 }
664 #pragma warning restore 618
665             }
666             else {
667                 if (this.conformanceLevel != baseReaderSettings.ConformanceLevel && this.conformanceLevel != ConformanceLevel.Auto) {
668                     throw new InvalidOperationException(Res.GetString(Res.Xml_IncompatibleConformanceLevel, this.conformanceLevel.ToString()));
669                 }
670                 if (this.checkCharacters && !baseReaderSettings.CheckCharacters) {
671                     checkChars = true;
672                     needWrap = true;
673                 }
674                 if (this.ignoreWhitespace && !baseReaderSettings.IgnoreWhitespace) {
675                     noWhitespace = true;
676                     needWrap = true;
677                 }
678                 if (this.ignoreComments && !baseReaderSettings.IgnoreComments) {
679                     noComments = true;
680                     needWrap = true;
681                 }
682                 if (this.ignorePIs && !baseReaderSettings.IgnoreProcessingInstructions) {
683                     noPIs = true;
684                     needWrap = true;
685                 }
686
687                 if ((this.dtdProcessing == DtdProcessing.Prohibit && baseReaderSettings.DtdProcessing != DtdProcessing.Prohibit) ||
688                     (this.dtdProcessing == DtdProcessing.Ignore && baseReaderSettings.DtdProcessing == DtdProcessing.Parse)) {
689                     dtdProc = this.dtdProcessing;
690                     needWrap = true;
691                 }
692             }
693
694             if (needWrap) {
695                 IXmlNamespaceResolver readerAsNSResolver = baseReader as IXmlNamespaceResolver;
696                 if (readerAsNSResolver != null) {
697                     return new XmlCharCheckingReaderWithNS(baseReader, readerAsNSResolver, checkChars, noWhitespace, noComments, noPIs, dtdProc);
698                 }
699                 else {
700                     return new XmlCharCheckingReader(baseReader, checkChars, noWhitespace, noComments, noPIs, dtdProc);
701                 }
702             }
703             else {
704                 return baseReader;
705             }
706         }
707
708 #if !SILVERLIGHT && !MOBILE
709         private static bool? s_enableLegacyXmlSettings = null;
710
711         static internal bool EnableLegacyXmlSettings()
712         {
713             if (s_enableLegacyXmlSettings.HasValue)
714             {
715                 return s_enableLegacyXmlSettings.Value;
716             }
717
718             if (!System.Xml.BinaryCompatibility.TargetsAtLeast_Desktop_V4_5_2)
719             {
720                 s_enableLegacyXmlSettings = true;
721                 return s_enableLegacyXmlSettings.Value;
722             }
723
724             bool enableSettings = false; // default value
725             if (!ReadSettingsFromRegistry(Registry.LocalMachine, ref enableSettings))
726             {
727                 // still ok if this call return false too as we'll use the default value which is false
728                 ReadSettingsFromRegistry(Registry.CurrentUser, ref enableSettings);
729             }
730
731             s_enableLegacyXmlSettings = enableSettings;
732             return s_enableLegacyXmlSettings.Value;
733         }
734
735         [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]
736         [SecuritySafeCritical]
737         private static bool ReadSettingsFromRegistry(RegistryKey hive, ref bool value)
738         {
739             const string regValueName = "EnableLegacyXmlSettings";
740             const string regValuePath = @"SOFTWARE\Microsoft\.NETFramework\XML";
741
742             try
743             {                                                                     
744                 using (RegistryKey xmlRegKey = hive.OpenSubKey(regValuePath, false))
745                 {
746                     if (xmlRegKey != null)
747                     {
748                         if (xmlRegKey.GetValueKind(regValueName) == RegistryValueKind.DWord)
749                         {
750                             value = ((int)xmlRegKey.GetValue(regValueName)) == 1;
751                             return true;
752                         }
753                     }
754                 }
755             }
756             catch { /* use the default if we couldn't read the key */ }
757
758             return false;
759         }
760
761 #endif // SILVERLIGHT
762         
763     }
764 }