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