1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7  **
8  ** Class: CfgParser
9  **
10  **
11  ** Purpose: XMLParser and Tree builder internal to BCL
12  **
13  **
14  ===========================================================*/
15 
16 namespace System
17 {
18     using System.Runtime.InteropServices;
19     using System.Collections;
20     using System.Collections.Generic;
21     using System.Runtime.CompilerServices;
22     using System.Security.Permissions;
23     using System.Security;
24     using System.Globalization;
25     using System.IO;
26     using System.Runtime.Versioning;
27     using System.Diagnostics.Contracts;
28 
29     [Serializable]
30     internal enum ConfigEvents
31     {
32         StartDocument     = 0,
33         StartDTD          = StartDocument + 1,
34         EndDTD            = StartDTD + 1,
35         StartDTDSubset    = EndDTD + 1,
36         EndDTDSubset      = StartDTDSubset + 1,
37         EndProlog         = EndDTDSubset + 1,
38         StartEntity       = EndProlog + 1,
39         EndEntity         = StartEntity + 1,
40         EndDocument       = EndEntity + 1,
41         DataAvailable     = EndDocument + 1,
42         LastEvent         = DataAvailable
43     }
44 
45     [Serializable]
46     internal enum ConfigNodeType
47     {
48         Element = 1,
49         Attribute   = Element + 1,
50         Pi  = Attribute + 1,
51         XmlDecl = Pi + 1,
52         DocType = XmlDecl + 1,
53         DTDAttribute    = DocType + 1,
54         EntityDecl  = DTDAttribute + 1,
55         ElementDecl = EntityDecl + 1,
56         AttlistDecl = ElementDecl + 1,
57         Notation    = AttlistDecl + 1,
58         Group   = Notation + 1,
59         IncludeSect = Group + 1,
60         PCData  = IncludeSect + 1,
61         CData   = PCData + 1,
62         IgnoreSect  = CData + 1,
63         Comment = IgnoreSect + 1,
64         EntityRef   = Comment + 1,
65         Whitespace  = EntityRef + 1,
66         Name    = Whitespace + 1,
67         NMToken = Name + 1,
68         String  = NMToken + 1,
69         Peref   = String + 1,
70         Model   = Peref + 1,
71         ATTDef  = Model + 1,
72         ATTType = ATTDef + 1,
73         ATTPresence = ATTType + 1,
74         DTDSubset   = ATTPresence + 1,
75         LastNodeType    = DTDSubset + 1
76     }
77 
78     [Serializable]
79     internal enum ConfigNodeSubType
80     {
81         Version = (int)ConfigNodeType.LastNodeType,
82         Encoding    = Version + 1,
83         Standalone  = Encoding + 1,
84         NS  = Standalone + 1,
85         XMLSpace    = NS + 1,
86         XMLLang = XMLSpace + 1,
87         System  = XMLLang + 1,
88         Public  = System + 1,
89         NData   = Public + 1,
90         AtCData = NData + 1,
91         AtId    = AtCData + 1,
92         AtIdref = AtId + 1,
93         AtIdrefs    = AtIdref + 1,
94         AtEntity    = AtIdrefs + 1,
95         AtEntities  = AtEntity + 1,
96         AtNmToken   = AtEntities + 1,
97         AtNmTokens  = AtNmToken + 1,
98         AtNotation  = AtNmTokens + 1,
99         AtRequired  = AtNotation + 1,
100         AtImplied   = AtRequired + 1,
101         AtFixed = AtImplied + 1,
102         PentityDecl = AtFixed + 1,
103         Empty   = PentityDecl + 1,
104         Any = Empty + 1,
105         Mixed   = Any + 1,
106         Sequence    = Mixed + 1,
107         Choice  = Sequence + 1,
108         Star    = Choice + 1,
109         Plus    = Star + 1,
110         Questionmark    = Plus + 1,
111         LastSubNodeType = Questionmark + 1
112     }
113 
114     internal abstract class BaseConfigHandler
115     {
116         // These delegates must be at the very start of the object
117         // This is necessary because unmanaged code takes a dependency on this layout
118         // Any changes made to this must be reflected in ConfigHelper.h in ConfigFactory class
119         protected Delegate[] eventCallbacks;
BaseConfigHandler()120         public BaseConfigHandler()
121         {
122             InitializeCallbacks();
123         }
InitializeCallbacks()124         private void InitializeCallbacks()
125         {
126             if (eventCallbacks == null)
127             {
128                 eventCallbacks = new Delegate[6];
129                 eventCallbacks[0] = new NotifyEventCallback(this.NotifyEvent);
130                 eventCallbacks[1] = new BeginChildrenCallback(this.BeginChildren);
131                 eventCallbacks[2] = new EndChildrenCallback(this.EndChildren);
132                 eventCallbacks[3] = new ErrorCallback(this.Error);
133                 eventCallbacks[4] = new CreateNodeCallback(this.CreateNode);
134                 eventCallbacks[5] = new CreateAttributeCallback(this.CreateAttribute);
135             }
136         }
137 
NotifyEventCallback(ConfigEvents nEvent)138         private delegate void NotifyEventCallback(ConfigEvents nEvent);
NotifyEvent(ConfigEvents nEvent)139         public abstract void NotifyEvent(ConfigEvents nEvent);
140 
BeginChildrenCallback(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)141         private delegate void BeginChildrenCallback(int size,
142                            ConfigNodeSubType subType,
143                            ConfigNodeType nType,
144                            int terminal,
145                            [MarshalAs(UnmanagedType.LPWStr)] String text,
146                            int textLength,
147                            int prefixLength);
BeginChildren(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)148         public abstract void BeginChildren(int size,
149                            ConfigNodeSubType subType,
150                            ConfigNodeType nType,
151                            int terminal,
152                            [MarshalAs(UnmanagedType.LPWStr)] String text,
153                            int textLength,
154                            int prefixLength);
155 
EndChildrenCallback(int fEmpty, int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)156         private delegate void EndChildrenCallback(int fEmpty,
157                          int size,
158                          ConfigNodeSubType subType,
159                          ConfigNodeType nType,
160                          int terminal,
161                          [MarshalAs(UnmanagedType.LPWStr)] String text,
162                          int textLength,
163                          int prefixLength);
EndChildren(int fEmpty, int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)164         public abstract void EndChildren(int fEmpty,
165                          int size,
166                          ConfigNodeSubType subType,
167                          ConfigNodeType nType,
168                          int terminal,
169                          [MarshalAs(UnmanagedType.LPWStr)] String text,
170                          int textLength,
171                          int prefixLength);
172 
ErrorCallback(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)173         private delegate void ErrorCallback(int size,
174                    ConfigNodeSubType subType,
175                    ConfigNodeType nType,
176                    int terminal,
177                    [MarshalAs(UnmanagedType.LPWStr)]String text,
178                    int textLength,
179                    int prefixLength);
Error(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)180         public abstract void Error(int size,
181                    ConfigNodeSubType subType,
182                    ConfigNodeType nType,
183                    int terminal,
184                    [MarshalAs(UnmanagedType.LPWStr)]String text,
185                    int textLength,
186                    int prefixLength);
187 
CreateNodeCallback(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)188         private delegate void CreateNodeCallback(int size,
189                         ConfigNodeSubType subType,
190                         ConfigNodeType nType,
191                         int terminal,
192                         [MarshalAs(UnmanagedType.LPWStr)]String text,
193                         int textLength,
194                         int prefixLength);
CreateNode(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)195         public abstract void CreateNode(int size,
196                         ConfigNodeSubType subType,
197                         ConfigNodeType nType,
198                         int terminal,
199                         [MarshalAs(UnmanagedType.LPWStr)]String text,
200                         int textLength,
201                         int prefixLength);
202 
CreateAttributeCallback(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)203         private delegate void CreateAttributeCallback(int size,
204                              ConfigNodeSubType subType,
205                              ConfigNodeType nType,
206                              int terminal,
207                              [MarshalAs(UnmanagedType.LPWStr)]String text,
208                              int textLength,
209                              int prefixLength);
CreateAttribute(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)210         public abstract void CreateAttribute(int size,
211                              ConfigNodeSubType subType,
212                              ConfigNodeType nType,
213                              int terminal,
214                              [MarshalAs(UnmanagedType.LPWStr)]String text,
215                              int textLength,
216                              int prefixLength);
217 
218         [System.Security.SecurityCritical]  // auto-generated
219         [ResourceExposure(ResourceScope.Machine)]
220         [MethodImplAttribute(MethodImplOptions.InternalCall)]
RunParser(String fileName)221         internal extern void RunParser(String fileName);
222     }
223 
224     // Class used to build a DOM like tree of parsed XML
225     internal class ConfigTreeParser : BaseConfigHandler
226     {
227         ConfigNode rootNode = null;
228         ConfigNode currentNode = null;
229         String fileName = null;
230         int attributeEntry;
231         String key = null;
232         String [] treeRootPath = null; // element to start tree
233         bool parsing = false;
234         int depth = 0;
235         int pathDepth = 0;
236         int searchDepth = 0;
237         bool bNoSearchPath = false;
238 
239         // Track state for error message formatting
240         String lastProcessed = null;
241         bool lastProcessedEndElement;
242 
243 
244         // NOTE: This parser takes a path eg. /configuration/system.runtime.remoting
245         // and will return a node which matches this.
246         [ResourceExposure(ResourceScope.Machine)]
247         [ResourceConsumption(ResourceScope.Machine)]
Parse(String fileName, String configPath)248         internal ConfigNode Parse(String fileName, String configPath)
249         {
250             return Parse(fileName, configPath, false);
251         }
252 
253         [System.Security.SecuritySafeCritical]  // auto-generated
254         [ResourceExposure(ResourceScope.Machine)]
255         [ResourceConsumption(ResourceScope.Machine)]
Parse(String fileName, String configPath, bool skipSecurityStuff)256         internal ConfigNode Parse(String fileName, String configPath, bool skipSecurityStuff)
257         {
258             if (fileName == null)
259                 throw new ArgumentNullException("fileName");
260             Contract.EndContractBlock();
261             this.fileName = fileName;
262             if (configPath[0] == '/'){
263                 treeRootPath = configPath.Substring(1).Split('/');
264                 pathDepth = treeRootPath.Length - 1;
265                 bNoSearchPath = false;
266             }
267             else{
268                 treeRootPath = new String[1];
269                 treeRootPath[0] = configPath;
270                 bNoSearchPath = true;
271             }
272 
273             if (!skipSecurityStuff) {
274                 (new FileIOPermission( FileIOPermissionAccess.Read, System.IO.Path.GetFullPathInternal( fileName ) )).Demand();
275             }
276 #pragma warning disable 618
277             (new SecurityPermission(SecurityPermissionFlag.UnmanagedCode)).Assert();
278 #pragma warning restore 618
279 
280             try
281             {
282                 RunParser(fileName);
283             }
284             catch(FileNotFoundException) {
285                 throw; // Pass these through unadulterated.
286             }
287             catch(DirectoryNotFoundException) {
288                 throw; // Pass these through unadulterated.
289             }
290             catch(UnauthorizedAccessException) {
291                 throw;
292             }
293             catch(FileLoadException) {
294                 throw;
295             }
296             catch(Exception inner) {
297                 String message = GetInvalidSyntaxMessage();
298                 // Neither Exception nor ApplicationException are the "right" exceptions here.
299                 // Desktop throws ApplicationException for backwards compatibility.
300                 // On Silverlight we don't have ApplicationException, so fall back to Exception.
301 #if FEATURE_CORECLR
302                 throw new Exception(message, inner);
303 #else
304                 throw new ApplicationException(message, inner);
305 #endif
306             }
307             return rootNode;
308         }
309 
NotifyEvent(ConfigEvents nEvent)310         public override void NotifyEvent(ConfigEvents nEvent)
311         {
312             BCLDebug.Trace("REMOTE", "NotifyEvent "+((Enum)nEvent).ToString()+"\n");
313         }
314 
BeginChildren(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)315         public override void BeginChildren(int size,
316                                   ConfigNodeSubType subType,
317                                   ConfigNodeType nType,
318                                   int terminal,
319                                   [MarshalAs(UnmanagedType.LPWStr)] String text,
320                                   int textLength,
321                                   int prefixLength)
322         {
323             //Trace("BeginChildren",size,subType,nType,terminal,text,textLength,prefixLength,0);
324             if (!parsing &&
325                 (!bNoSearchPath
326                  && depth == (searchDepth + 1)
327                  && String.Compare(text, treeRootPath[searchDepth], StringComparison.Ordinal) == 0))
328             {
329                 searchDepth++;
330             }
331         }
332 
EndChildren(int fEmpty, int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)] String text, int textLength, int prefixLength)333         public override void EndChildren(int fEmpty,
334                                 int size,
335                                 ConfigNodeSubType subType,
336                                 ConfigNodeType nType,
337                                 int terminal,
338                                 [MarshalAs(UnmanagedType.LPWStr)] String text,
339                                 int textLength,
340                                 int prefixLength)
341         {
342             lastProcessed = text;
343             lastProcessedEndElement = true;
344             if (parsing)
345             {
346                 //Trace("EndChildren",size,subType,nType,terminal,text,textLength,prefixLength,fEmpty);
347 
348                 if (currentNode == rootNode)
349                 {
350                     // End of section of tree which is parsed
351                     parsing = false;
352                 }
353 
354                 currentNode = currentNode.Parent;
355             }
356             else if (nType == ConfigNodeType.Element){
357                 if(depth == searchDepth && String.Compare(text, treeRootPath[searchDepth - 1], StringComparison.Ordinal) == 0)
358                 {
359                     searchDepth--;
360                     depth--;
361                 }
362                 else
363                     depth--;
364             }
365         }
366 
Error(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)367         public override void Error(int size,
368                           ConfigNodeSubType subType,
369                           ConfigNodeType nType,
370                           int terminal,
371                           [MarshalAs(UnmanagedType.LPWStr)]String text,
372                           int textLength,
373                           int prefixLength)
374         {
375             //Trace("Error",size,subType,nType,terminal,text,textLength,prefixLength,0);
376         }
377 
CreateNode(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)378         public override void CreateNode(int size,
379                                ConfigNodeSubType subType,
380                                ConfigNodeType nType,
381                                int terminal,
382                                [MarshalAs(UnmanagedType.LPWStr)]String text,
383                                int textLength,
384                                int prefixLength)
385         {
386             //Trace("CreateNode",size,subType,nType,terminal,text,textLength,prefixLength,0);
387 
388             if (nType == ConfigNodeType.Element)
389             {
390                 // New Node
391                 lastProcessed = text;
392                 lastProcessedEndElement = false;
393 
394                 if (parsing
395                     || (bNoSearchPath &&
396                         String.Compare(text, treeRootPath[0], StringComparison.OrdinalIgnoreCase) == 0)
397                     || (depth == searchDepth && searchDepth == pathDepth &&
398                         String.Compare(text, treeRootPath[pathDepth], StringComparison.OrdinalIgnoreCase) == 0 ))
399                     {
400                         parsing = true;
401 
402                         ConfigNode parentNode = currentNode;
403                         currentNode = new ConfigNode(text, parentNode);
404                         if (rootNode == null)
405                             rootNode = currentNode;
406                         else
407                             parentNode.AddChild(currentNode);
408                     }
409                 else
410                     depth++;
411             }
412             else if (nType == ConfigNodeType.PCData)
413             {
414                 // Data node
415                 if (currentNode != null)
416                 {
417                     currentNode.Value = text;
418                 }
419             }
420         }
421 
CreateAttribute(int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength)422         public override void CreateAttribute(int size,
423                                     ConfigNodeSubType subType,
424                                     ConfigNodeType nType,
425                                     int terminal,
426                                     [MarshalAs(UnmanagedType.LPWStr)]String text,
427                                     int textLength,
428                                     int prefixLength)
429         {
430             //Trace("CreateAttribute",size,subType,nType,terminal,text,textLength,prefixLength,0);
431             if (parsing)
432             {
433                 // if the value of the attribute is null, the parser doesn't come back, so need to store the attribute when the
434                 // attribute name is encountered
435                 if (nType == ConfigNodeType.Attribute)
436                 {
437                     attributeEntry = currentNode.AddAttribute(text, "");
438                     key = text;
439                 }
440                 else if (nType == ConfigNodeType.PCData)
441                 {
442                     currentNode.ReplaceAttribute(attributeEntry, key, text);
443                 }
444                 else
445                 {
446                     String message = GetInvalidSyntaxMessage();
447                     // Neither Exception nor ApplicationException are the "right" exceptions here.
448                     // Desktop throws ApplicationException for backwards compatibility.
449                     // On Silverlight we don't have ApplicationException, so fall back to Exception.
450 #if FEATURE_CORECLR
451                     throw new Exception(message);
452 #else
453                     throw new ApplicationException(message);
454 #endif
455                 }
456             }
457         }
458 
459 #if _DEBUG
460         [System.Diagnostics.Conditional("_LOGGING")]
Trace(String name, int size, ConfigNodeSubType subType, ConfigNodeType nType, int terminal, [MarshalAs(UnmanagedType.LPWStr)]String text, int textLength, int prefixLength, int fEmpty)461         private void Trace(String name,
462                            int size,
463                            ConfigNodeSubType subType,
464                            ConfigNodeType nType,
465                            int terminal,
466                            [MarshalAs(UnmanagedType.LPWStr)]String text,
467                            int textLength,
468                            int prefixLength, int fEmpty)
469         {
470 
471             BCLDebug.Trace("REMOTE","Node "+name);
472             BCLDebug.Trace("REMOTE","text "+text);
473             BCLDebug.Trace("REMOTE","textLength "+textLength);
474             BCLDebug.Trace("REMOTE","size "+size);
475             BCLDebug.Trace("REMOTE","subType "+((Enum)subType).ToString());
476             BCLDebug.Trace("REMOTE","nType "+((Enum)nType).ToString());
477             BCLDebug.Trace("REMOTE","terminal "+terminal);
478             BCLDebug.Trace("REMOTE","prefixLength "+prefixLength);
479             BCLDebug.Trace("REMOTE","fEmpty "+fEmpty+"\n");
480         }
481 #endif
482 
GetInvalidSyntaxMessage()483         private String GetInvalidSyntaxMessage()
484         {
485             String lastProcessedTag = null;
486 
487             if (lastProcessed != null)
488                 lastProcessedTag = (lastProcessedEndElement ? "</" : "<") + lastProcessed + ">";
489 
490             return Environment.GetResourceString("XML_Syntax_InvalidSyntaxInFile", fileName, lastProcessedTag);
491         }
492     }
493 
494     // Node in Tree produced by ConfigTreeParser
495     internal class ConfigNode
496     {
497         String m_name = null;
498         String m_value = null;
499         ConfigNode m_parent = null;
500         List<ConfigNode> m_children = new List<ConfigNode>(5);
501         List<DictionaryEntry> m_attributes = new List<DictionaryEntry>(5);
502 
ConfigNode(String name, ConfigNode parent)503         internal ConfigNode(String name, ConfigNode parent)
504         {
505             m_name = name;
506             m_parent = parent;
507         }
508 
509         internal String Name
510         {
511             get {return m_name;}
512         }
513 
514         internal String Value
515         {
516             get {return m_value;}
517             set {m_value = value;}
518         }
519 
520         internal ConfigNode Parent
521         {
522             get {return m_parent;}
523         }
524 
525         internal List<ConfigNode> Children
526         {
527             get {return m_children;}
528         }
529 
530         internal List<DictionaryEntry> Attributes
531         {
532             get {return m_attributes;}
533         }
534 
AddChild(ConfigNode child)535         internal void AddChild(ConfigNode child)
536         {
537             child.m_parent = this;
538             m_children.Add(child);
539         }
540 
AddAttribute(String key, String value)541         internal int AddAttribute(String key, String value)
542         {
543             m_attributes.Add(new DictionaryEntry(key, value));
544             return m_attributes.Count-1;
545         }
546 
ReplaceAttribute(int index, String key, String value)547         internal void ReplaceAttribute(int index, String key, String value)
548         {
549             m_attributes[index] = new DictionaryEntry(key, value);
550         }
551 
552 #if _DEBUG
553         [System.Diagnostics.Conditional("_LOGGING")]
Trace()554         internal void Trace()
555         {
556             BCLDebug.Trace("REMOTE","************ConfigNode************");
557             BCLDebug.Trace("REMOTE","Name = "+m_name);
558             if (m_value != null)
559                 BCLDebug.Trace("REMOTE","Value = "+m_value);
560             if (m_parent != null)
561                 BCLDebug.Trace("REMOTE","Parent = "+m_parent.Name);
562             for (int i=0; i<m_attributes.Count; i++)
563             {
564                 DictionaryEntry de = (DictionaryEntry)m_attributes[i];
565                 BCLDebug.Trace("REMOTE","Key = "+de.Key+"   Value = "+de.Value);
566             }
567 
568             for (int i=0; i<m_children.Count; i++)
569             {
570                 ((ConfigNode)m_children[i]).Trace();
571             }
572         }
573 #endif
574     }
575 }
576 
577 
578 
579 
580 
581 
582