[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Configuration / System / Configuration / BaseConfigurationRecord.cs
blob831c5ba22827b30e58d524a8eeb0afbb55fdee3b
1 //------------------------------------------------------------------------------
2 // <copyright file="BaseConfigurationRecord.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
8 namespace System.Configuration {
9 using System.Configuration.Internal;
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Collections.Specialized;
13 using System.Configuration;
14 using System.Globalization;
15 using System.IO;
16 using System.Reflection;
17 using System.Runtime.InteropServices;
18 using System.Security.Permissions;
19 using System.Security;
20 using System.Text;
21 using System.Threading;
22 using System.Xml;
23 using System.Runtime.Versioning;
26 // This object represents the configuration for a request path, and is cached per-path.
29 [System.Diagnostics.DebuggerDisplay("ConfigPath = {ConfigPath}")]
30 abstract internal class BaseConfigurationRecord : IInternalConfigRecord {
31 protected const string NL = "\r\n";
33 internal const string KEYWORD_TRUE = "true";
34 internal const string KEYWORD_FALSE = "false";
35 protected const string KEYWORD_CONFIGURATION = "configuration";
36 protected const string KEYWORD_CONFIGURATION_NAMESPACE = "http://schemas.microsoft.com/.NetConfiguration/v2.0";
37 protected const string KEYWORD_CONFIGSECTIONS = "configSections";
38 protected const string KEYWORD_SECTION = "section";
39 protected const string KEYWORD_SECTION_NAME = "name";
40 protected const string KEYWORD_SECTION_TYPE = "type";
41 protected const string KEYWORD_SECTION_ALLOWLOCATION = "allowLocation";
42 protected const string KEYWORD_SECTION_ALLOWDEFINITION = "allowDefinition";
43 protected const string KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE = "Everywhere";
44 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY = "MachineOnly";
45 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION = "MachineToApplication";
46 protected const string KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT = "MachineToWebRoot";
47 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION = "allowExeDefinition";
48 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING = "MachineToRoamingUser";
49 protected const string KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL = "MachineToLocalUser";
50 protected const string KEYWORD_SECTION_RESTARTONEXTERNALCHANGES = "restartOnExternalChanges";
51 protected const string KEYWORD_SECTION_REQUIREPERMISSION = "requirePermission";
52 protected const string KEYWORD_SECTIONGROUP = "sectionGroup";
53 protected const string KEYWORD_SECTIONGROUP_NAME = "name";
54 protected const string KEYWORD_SECTIONGROUP_TYPE = "type";
55 protected const string KEYWORD_REMOVE = "remove";
56 protected const string KEYWORD_CLEAR = "clear";
57 protected const string KEYWORD_LOCATION = "location";
58 protected const string KEYWORD_LOCATION_PATH = "path";
59 internal const string KEYWORD_LOCATION_ALLOWOVERRIDE = "allowOverride";
60 protected const string KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS = "inheritInChildApplications";
61 protected const string KEYWORD_CONFIGSOURCE = "configSource";
62 protected const string KEYWORD_XMLNS = "xmlns";
63 protected const string KEYWORD_CONFIG_BUILDER = "configBuilders";
64 internal const string KEYWORD_PROTECTION_PROVIDER = "configProtectionProvider";
65 protected const string FORMAT_NEWCONFIGFILE = "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";
66 protected const string FORMAT_CONFIGURATION = "<configuration>\r\n";
67 protected const string FORMAT_CONFIGURATION_NAMESPACE = "<configuration xmlns=\"{0}\">\r\n";
68 protected const string FORMAT_CONFIGURATION_ENDELEMENT = "</configuration>";
69 internal const string KEYWORD_SECTION_OVERRIDEMODEDEFAULT = "overrideModeDefault";
70 internal const string KEYWORD_LOCATION_OVERRIDEMODE = "overrideMode";
71 internal const string KEYWORD_OVERRIDEMODE_INHERIT = "Inherit";
72 internal const string KEYWORD_OVERRIDEMODE_ALLOW = "Allow";
73 internal const string KEYWORD_OVERRIDEMODE_DENY = "Deny";
76 protected const string FORMAT_LOCATION_NOPATH = "<location {0} inheritInChildApplications=\"{1}\">\r\n";
77 protected const string FORMAT_LOCATION_PATH = "<location path=\"{2}\" {0} inheritInChildApplications=\"{1}\">\r\n";
78 protected const string FORMAT_LOCATION_ENDELEMENT = "</location>";
79 internal const string KEYWORD_LOCATION_OVERRIDEMODE_STRING = "{0}=\"{1}\"";
81 protected const string FORMAT_SECTION_CONFIGSOURCE = "<{0} configSource=\"{1}\" />";
82 protected const string FORMAT_CONFIGSOURCE_FILE = "<?xml version=\"1.0\" encoding=\"{0}\"?>\r\n";
84 protected const string FORMAT_SECTIONGROUP_ENDELEMENT = "</sectionGroup>";
86 // Class flags should only be used with the ClassFlags property.
87 protected const int ClassSupportsChangeNotifications = 0x00000001;
88 protected const int ClassSupportsRefresh = 0x00000002;
89 protected const int ClassSupportsImpersonation = 0x00000004;
90 protected const int ClassSupportsRestrictedPermissions = 0x00000008;
91 protected const int ClassSupportsKeepInputs = 0x00000010;
92 protected const int ClassSupportsDelayedInit = 0x00000020;
93 protected const int ClassIgnoreLocalErrors = 0x00000040;
95 // Flags to use with the _flags field.
96 protected const int ProtectedDataInitialized = 0x00000001;
97 protected const int Closed = 0x00000002;
98 protected const int PrefetchAll = 0x00000008;
99 protected const int IsAboveApplication = 0x00000020;
100 private const int ContextEvaluated = 0x00000080;
101 private const int IsLocationListResolved = 0x00000100;
102 protected const int NamespacePresentInFile = 0x00000200;
104 private const int RestrictedPermissionsResolved = 0x00000800;
106 protected const int IsTrusted = 0x00002000;
108 protected const int SupportsChangeNotifications = 0x00010000;
109 protected const int SupportsRefresh = 0x00020000;
110 protected const int SupportsPath = 0x00040000;
111 protected const int SupportsKeepInputs = 0x00080000;
112 protected const int SupportsLocation = 0x00100000;
114 // Flags for Mgmt Configuration Record
115 protected const int ForceLocationWritten = 0x01000000;
116 protected const int SuggestLocationRemoval = 0x02000000;
117 protected const int NamespacePresentCurrent = 0x04000000;
119 protected const int ConfigBuildersInitialized = 0x08000000;
121 internal const char ConfigPathSeparatorChar = '/';
122 internal const string ConfigPathSeparatorString = "/";
123 static internal readonly char[] ConfigPathSeparatorParams = new char[] {ConfigPathSeparatorChar};
125 private static volatile ConfigurationPermission s_unrestrictedConfigPermission; // cached ConfigurationPermission
127 protected SafeBitVector32 _flags; // state
128 protected BaseConfigurationRecord _parent; // parent record
129 protected Hashtable _children; // configName -> record
130 protected InternalConfigRoot _configRoot; // root of configuration
131 protected string _configName; // the last part of the config path
132 protected string _configPath; // the full config path
133 protected string _locationSubPath; // subPath for the config record when editing a location configuration
135 private ConfigRecordStreamInfo _configStreamInfo; // stream info for the config record
137 private object _configContext; // Context for config level
138 private ConfigurationBuildersSection _configBuilders; // section containing the general config builders
139 private ProtectedConfigurationSection _protectedConfig; // section containing the encryption providers
140 private PermissionSet _restrictedPermissions; // cached restricted permission set
141 private ConfigurationSchemaErrors _initErrors; // errors encountered during the parse of the configuration file
142 private BaseConfigurationRecord _initDelayedRoot; // root of delayed initialization
144 // Records information about <configSections> present in this web.config file.
145 // config key -> FactoryRecord
146 protected Hashtable _factoryRecords;
148 // Records information about sections that apply to this path,
149 // which may be found in this web.config file, in a parent through
150 // inheritance, or in a parent through <location>
151 // config key -> SectionRecord
152 protected Hashtable _sectionRecords;
154 // Records information about sections in a <location> directive
155 // that do not apply to this configPath (sections where path != ".")
156 protected ArrayList _locationSections;
158 // WOS 1955773: (Perf) 4,000 location sections in web.config file degrades working set
159 private static string s_appConfigPath;
161 // Comparer used in sorting IndirectInputs.
162 private static IComparer<SectionInput> s_indirectInputsComparer = new IndirectLocationInputComparer();
164 internal BaseConfigurationRecord() {
165 // not strictly necessary, but compiler spits out a warning without this initiailization
166 _flags = new SafeBitVector32();
169 // Class flags
170 protected abstract SimpleBitVector32 ClassFlags {get;}
172 // Create the factory that will evaluate configuration
173 protected abstract object CreateSectionFactory(FactoryRecord factoryRecord);
175 // Create the configuration object
176 protected abstract object CreateSection(bool inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, SectionInput sectionInput, object parentConfig, ConfigXmlReader reader);
178 // Use the parent result in creating the child
179 protected abstract object UseParentResult(string configKey, object parentResult, SectionRecord sectionRecord);
181 // Return the runtime object from GetSection
182 protected abstract object GetRuntimeObject(object result);
185 // IInternalConfigRecord methods
188 public string ConfigPath {
189 get {return _configPath;}
192 public string StreamName {
193 get {return ConfigStreamInfo.StreamName;}
196 public bool HasInitErrors {
197 get {
198 return _initErrors.HasErrors(ClassFlags[ClassIgnoreLocalErrors]);
202 public void ThrowIfInitErrors() {
203 ThrowIfParseErrors(_initErrors);
206 public object GetSection(string configKey) {
207 #if DBG
208 // On debug builds, the config system depends on system.diagnostics,
209 // so we must always return a valid result and never throw.
210 if (configKey == "system.diagnostics" && !ClassFlags[ClassIgnoreLocalErrors]) {
211 return GetSection(configKey, true, true);
213 else {
214 return GetSection(configKey, false, true);
216 #else
218 return GetSection(configKey, false, true);
219 #endif
222 public object GetLkgSection(string configKey) {
223 return GetSection(configKey, true, true);
226 public void RefreshSection(string configKey) {
227 _configRoot.ClearResult(this, configKey, true);
230 public void Remove() {
231 _configRoot.RemoveConfigRecord(this);
235 // end of IInternalConfigRecord methods
238 internal bool HasStream {
239 get { return ConfigStreamInfo.HasStream; }
242 // Determine which sections should be prefetched during the first scan.
243 private bool ShouldPrefetchRawXml(FactoryRecord factoryRecord) {
244 if (_flags[PrefetchAll])
245 return true;
247 switch (factoryRecord.ConfigKey) {
248 case BaseConfigurationRecord.RESERVED_SECTION_PROTECTED_CONFIGURATION:
249 case "system.diagnostics":
250 case "appSettings":
251 case "connectionStrings":
252 return true;
255 return Host.PrefetchSection(factoryRecord.Group, factoryRecord.Name);
258 protected IDisposable Impersonate() {
259 IDisposable context = null;
260 if (ClassFlags[ClassSupportsImpersonation]) {
261 context = Host.Impersonate();
264 if (context == null) {
265 context = EmptyImpersonationContext.GetStaticInstance();
268 return context;
271 internal PermissionSet GetRestrictedPermissions()
273 if (!_flags[RestrictedPermissionsResolved])
275 lock (this)
277 if (!_flags[RestrictedPermissionsResolved])
279 if (AppDomain.CurrentDomain.IsHomogenous)
281 // in a homogenous domain, just call PermitOnly() on the AppDomain's existing permission set
282 _restrictedPermissions = AppDomain.CurrentDomain.PermissionSet;
283 _flags[RestrictedPermissionsResolved] = true;
285 else
287 // in a non-homogenous domain, use Evidence to calculate the current security policy
288 PermissionSet restrictedPermissions;
289 bool isHostReady;
291 GetRestrictedPermissionsWithAssert(out restrictedPermissions, out isHostReady);
292 if (isHostReady)
294 _restrictedPermissions = restrictedPermissions;
295 _flags[RestrictedPermissionsResolved] = true;
299 // calling PermitOnly() on an unrestricted PermissionSet is a no-op
300 if (_restrictedPermissions != null && _restrictedPermissions.IsUnrestricted())
302 _restrictedPermissions = null;
313 return _restrictedPermissions;
315 // Getting the PermissionSet object is a FullTrust operation; we can Assert since our callers are careful not to leak it
316 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
317 private void GetRestrictedPermissionsWithAssert(out PermissionSet permissionSet, out bool isHostReady) {
318 Host.GetRestrictedPermissions(this, out permissionSet, out isHostReady);
321 internal void Init(
322 IInternalConfigRoot configRoot,
323 BaseConfigurationRecord parent,
324 string configPath,
325 string locationSubPath) {
327 _initErrors = new ConfigurationSchemaErrors();
330 // try/catch here is only for unexpected exceptions due to errors in
331 // our own code, as we always want the configuration record to be
332 // usable
334 try {
335 _configRoot = (InternalConfigRoot) configRoot;
336 _parent = parent;
337 _configPath = configPath;
338 _locationSubPath = locationSubPath;
339 _configName = ConfigPathUtility.GetName(configPath);
341 if (IsLocationConfig) {
342 _configStreamInfo = _parent.ConfigStreamInfo;
344 else {
345 _configStreamInfo = new ConfigRecordStreamInfo();
348 // no more initialization in case of root config
349 if (IsRootConfig)
350 return;
352 // determine what features we support
353 _flags[SupportsChangeNotifications] = ClassFlags[ClassSupportsChangeNotifications] && Host.SupportsChangeNotifications;
354 _flags[SupportsRefresh] = ClassFlags[ClassSupportsRefresh] && Host.SupportsRefresh;
355 _flags[SupportsKeepInputs] = ClassFlags[ClassSupportsKeepInputs] || _flags[SupportsRefresh];
356 _flags[SupportsPath] = Host.SupportsPath;
357 _flags[SupportsLocation] = Host.SupportsLocation;
359 // get static state based on the configPath
360 if (_flags[SupportsLocation]) {
361 _flags[IsAboveApplication] = Host.IsAboveApplication(_configPath);
364 _flags[IsTrusted] = Host.IsTrustedConfigPath(_configPath);
366 ArrayList locationSubPathInputs = null;
368 if (_flags[SupportsLocation]) {
370 // Treat location inputs from parent record
371 // as though they were bonafide sections in this record.
373 if (IsLocationConfig && _parent._locationSections != null) {
374 // Resolve paths and check for errors in location sections.
375 _parent.ResolveLocationSections();
377 int i = 0;
378 while (i < _parent._locationSections.Count) {
379 LocationSectionRecord locationSectionRecord = (LocationSectionRecord) _parent._locationSections[i];
381 if (!StringUtil.EqualsIgnoreCase(locationSectionRecord.SectionXmlInfo.TargetConfigPath, this.ConfigPath)) {
383 i++;
385 else {
386 // remove the locationSectionRecord from the list
387 _parent._locationSections.RemoveAt(i);
389 if (locationSubPathInputs == null) {
390 locationSubPathInputs = new ArrayList();
393 locationSubPathInputs.Add(locationSectionRecord);
398 // Handle indirect inputs for location config record.
399 if (IsLocationConfig && Host.IsLocationApplicable(_configPath)) {
400 Dictionary<string, List<SectionInput>> indirectLocationInputs = null;
401 BaseConfigurationRecord current = _parent;
403 // Note that the code will also go thru all parents, just like what we did
404 // with the location inputs later. But perf-wise it's okay not to merge the two loops
405 // because a Configuration object will contain at most one location config record.
406 while (!current.IsRootConfig) {
407 if (current._locationSections != null) {
408 current.ResolveLocationSections();
410 foreach (LocationSectionRecord locationSectionRecord in current._locationSections) {
412 // VSWhidbey 540184
413 // If we're initializing a location config record, we will also use those location
414 // tags that apply to the directories between the parent and the location config
415 // record's ConfigPath.
417 // This way, the result will contain all the inputs (we've parsed so far) which
418 // affect the path specified by locationSubPath.
420 // To look for this kind of input, make sure that:
421 // 1. We are creating a location config
422 // 2. The location tag's target path lies between parent's config path and ConfigPath
423 // 2.1: ConfigPath must be a child of the location tag's target path
424 // 2.2: The target path must be a child of parent's config path
426 // One example of why we need 2.2:
427 // The root web.config has a location tag with path="1", and we open a configuration
428 // at "1" with locationSubPath="sub". If we don't have 2.2, then we will incorrectly set
429 // addInput to true for this location tag. It's incorrect because that location tag
430 // is already used by configRecord at level "1".
432 // 3. The location tag shouldn't be skipped due to inheritInChildApplications setting
434 if (
435 // Check #1
436 IsLocationConfig &&
438 // Check #2.1
439 UrlPath.IsSubpath(locationSectionRecord.SectionXmlInfo.TargetConfigPath, ConfigPath) &&
441 // Check #2.2
442 UrlPath.IsSubpath(parent.ConfigPath, locationSectionRecord.SectionXmlInfo.TargetConfigPath) &&
444 // Check #3
445 !ShouldSkipDueToInheritInChildApplications(locationSectionRecord.SectionXmlInfo.SkipInChildApps, locationSectionRecord.SectionXmlInfo.TargetConfigPath)
449 // In order to separate these kinds of input from "file inputs" and "location inputs"
450 // we introduce a new kind of input called the "indirect location inputs".
453 // First add all indirect inputs per configKey to a local list.
454 // We will sort all lists after the while loop.
455 if (indirectLocationInputs == null) {
456 indirectLocationInputs = new Dictionary<string, List<SectionInput>>(1);
459 string configKey = locationSectionRecord.SectionXmlInfo.ConfigKey;
461 if (!((IDictionary)indirectLocationInputs).Contains(configKey)) {
462 indirectLocationInputs.Add(configKey, new List<SectionInput>(1));
465 indirectLocationInputs[configKey].Add(
466 new SectionInput(locationSectionRecord.SectionXmlInfo,
467 locationSectionRecord.ErrorsList));
469 // copy the initialization errors to the record
470 if (locationSectionRecord.HasErrors) {
471 this._initErrors.AddSavedLocalErrors(locationSectionRecord.Errors);
477 current = current._parent;
480 if (indirectLocationInputs != null) {
481 // Add indirect inputs per configKey
482 foreach(KeyValuePair<string, List<SectionInput>> keyValuePair in indirectLocationInputs) {
483 List<SectionInput> inputsPerConfigKey = keyValuePair.Value;
484 string configKey = keyValuePair.Key;
486 // We have to sort the indirect inputs
487 // 1. First by the location tag's target config path, and if they're the same,
488 // 2. Then by the location tag's definition config path.
489 inputsPerConfigKey.Sort(s_indirectInputsComparer);
491 // Add them to the section record.
492 // In the sorted list, the closest parent is at the beginning of the
493 // list, which is what we'll add first.
494 SectionRecord sectionRecord = EnsureSectionRecord(configKey, true);
495 Debug.Assert(inputsPerConfigKey.Count > 0, "We won't get here unless we have inputs.");
496 foreach(SectionInput sectionInput in inputsPerConfigKey) {
497 sectionRecord.AddIndirectLocationInput(sectionInput);
500 DebugValidateIndirectInputs(sectionRecord);
506 // Add location inputs that apply to this path, all the way up the parent hierarchy.
508 if (Host.IsLocationApplicable(_configPath)) {
509 BaseConfigurationRecord current = _parent;
510 while (!current.IsRootConfig) {
511 if (current._locationSections != null) {
512 current.ResolveLocationSections();
513 foreach (LocationSectionRecord locationSectionRecord in current._locationSections) {
514 // We use the location tag input if:
515 // - The path matches, and
516 // - It's not skipped due to inheritInChildApplications setting.
517 if (StringUtil.EqualsIgnoreCase(locationSectionRecord.SectionXmlInfo.TargetConfigPath, this._configPath) &&
518 !ShouldSkipDueToInheritInChildApplications(locationSectionRecord.SectionXmlInfo.SkipInChildApps)) {
519 // add the location input for this section
520 SectionRecord sectionRecord = EnsureSectionRecord(locationSectionRecord.ConfigKey, true);
521 SectionInput sectionInput = new SectionInput(
522 locationSectionRecord.SectionXmlInfo, locationSectionRecord.ErrorsList);
524 sectionRecord.AddLocationInput(sectionInput);
526 // copy the initialization errors to the record
527 if (locationSectionRecord.HasErrors) {
528 this._initErrors.AddSavedLocalErrors(locationSectionRecord.Errors);
534 current = current._parent;
540 if (!IsLocationConfig) {
542 // If config file exists, open it and parse it once so we can
543 // find what to evaluate later.
545 InitConfigFromFile();
547 else {
548 // Add location sections for this record as bonafide sections.
549 if (locationSubPathInputs != null) {
550 foreach (LocationSectionRecord locationSectionRecord in locationSubPathInputs) {
551 // add the file input
552 SectionRecord sectionRecord = EnsureSectionRecord(locationSectionRecord.ConfigKey, true);
553 SectionInput sectionInput = new SectionInput(locationSectionRecord.SectionXmlInfo, locationSectionRecord.ErrorsList);
554 sectionRecord.AddFileInput(sectionInput);
556 // copy the initialization errors to the record
557 if (locationSectionRecord.HasErrors) {
558 this._initErrors.AddSavedLocalErrors(locationSectionRecord.Errors);
565 // Capture all exceptions and include them in initialization errors.
567 catch (Exception e) {
568 string streamname = (ConfigStreamInfo != null) ? ConfigStreamInfo.StreamName : null;
570 _initErrors.AddError(
571 ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, streamname, 0),
572 ExceptionAction.Global);
576 // InitConfigFromFile
578 // Init the Config From the File.
580 private void InitConfigFromFile() {
581 bool implicitSectionsAdded = false;
583 try {
584 // If initialization should be delayed, do not read the file.
585 if (ClassFlags[ClassSupportsDelayedInit] && Host.IsInitDelayed(this)) {
586 // determine the root of delayed initialization
587 if (_parent._initDelayedRoot == null) {
588 _initDelayedRoot = this;
590 else {
591 _initDelayedRoot = _parent._initDelayedRoot;
594 else {
596 // This parent of a record that is not initDelayed must also
597 // not be initDelayed.
599 Debug.Assert(!_parent.IsInitDelayed, "!_parent.IsInitDelayed");
601 using (Impersonate()) {
604 // Get the stream name. Note that this may be an expensive operation
605 // for the client configuration host, which is why it comes after the
606 // check for delayed init.
608 ConfigStreamInfo.StreamName = Host.GetStreamName(_configPath);
609 if (!String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
610 // Lets listen to the stream
611 ConfigStreamInfo.StreamVersion = MonitorStream(null, null, ConfigStreamInfo.StreamName);
612 using (Stream stream = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) {
613 if (stream == null) {
614 // There is no stream to load from
615 return;
618 ConfigStreamInfo.HasStream = true;
620 // Determine whether or not to prefetch.
621 _flags[PrefetchAll] = Host.PrefetchAll(_configPath, ConfigStreamInfo.StreamName);
623 using (XmlUtil xmlUtil = new XmlUtil(stream, ConfigStreamInfo.StreamName, true, _initErrors)) {
624 ConfigStreamInfo.StreamEncoding = xmlUtil.Reader.Encoding;
626 // Read the factories
627 Hashtable factoryList = ScanFactories(xmlUtil);
628 _factoryRecords = factoryList;
630 // Add implicit sections before scanning sections
631 AddImplicitSections(null);
632 implicitSectionsAdded = true;
634 // Read the sections themselves
635 if (xmlUtil.Reader.Depth == 1) {
636 ScanSections(xmlUtil);
645 // Capture all exceptions and include them in _initErrors so execution in Init can continue.
647 catch (XmlException e) {
649 // Treat invalid XML as unrecoverable by replacing all errors with the XML error.
651 _initErrors.SetSingleGlobalError(
652 ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0));
654 catch (Exception e) {
655 _initErrors.AddError(
656 ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, 0),
657 ExceptionAction.Global);
661 // If there are global errors that make all input invalid,
662 // reset our state so that inherited configuration can still be used,
663 // including location sections that apply to this file.
665 if (_initErrors.HasGlobalErrors) {
668 // Parsing of a section may have been in progress when the exception
669 // was thrown, so clear any accumulated local errors.
671 _initErrors.ResetLocalErrors();
674 // Stop monitoring any configSource streams, but still continue
675 // to monitor the main config file if it was successfully monitored.
677 HybridDictionary streamInfos = null;
678 lock (this) {
679 if (ConfigStreamInfo.HasStreamInfos) {
680 streamInfos = ConfigStreamInfo.StreamInfos;
681 ConfigStreamInfo.ClearStreamInfos();
683 if (!String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
684 StreamInfo fileStreamInfo = (StreamInfo) streamInfos[ConfigStreamInfo.StreamName];
686 // add this file's streaminfo to the now empty list
687 if (fileStreamInfo != null) {
688 streamInfos.Remove(ConfigStreamInfo.StreamName);
689 ConfigStreamInfo.StreamInfos.Add(ConfigStreamInfo.StreamName, fileStreamInfo);
695 // Stop monitoring streams outside the lock, to prevent deadlock.
696 if (streamInfos != null) {
697 foreach (StreamInfo streamInfo in streamInfos.Values) {
698 if (streamInfo.IsMonitored) {
699 Host.StopMonitoringStreamForChanges(streamInfo.StreamName, ConfigStreamInfo.CallbackDelegate);
704 // Remove file input
705 if (_sectionRecords != null) {
706 List<SectionRecord> removes = null;
707 foreach (SectionRecord sectionRecord in _sectionRecords.Values) {
708 Debug.Assert(!sectionRecord.HasIndirectLocationInputs,
709 "Don't expect to have any IndirectLocationInputs because location config record shouldn't call InitConfigFromFile");
711 if (sectionRecord.HasLocationInputs) {
712 // Remove any file input
713 sectionRecord.RemoveFileInput();
715 else {
716 // Remove the entire section record
717 if (removes == null) {
718 removes = new List<SectionRecord>();
721 removes.Add(sectionRecord);
725 if (removes != null) {
726 foreach (SectionRecord sectionRecord in removes) {
727 _sectionRecords.Remove(sectionRecord.ConfigKey);
732 // Remove all location section input defined here
733 if (_locationSections != null) {
734 _locationSections.Clear();
737 // Remove all factory records
738 if (_factoryRecords != null) {
739 _factoryRecords.Clear();
743 if (!implicitSectionsAdded) {
744 // Always add implicit sections no matter we have a file or not.
745 AddImplicitSections(null);
749 private bool IsInitDelayed {
750 get {
751 return _initDelayedRoot != null;
755 // RefreshFactoryRecord
757 // Refresh the Factory Record for a particular section.
759 private void RefreshFactoryRecord(string configKey) {
760 Hashtable factoryList = null;
761 FactoryRecord factoryRecord = null;
762 ConfigurationSchemaErrors errors;
764 errors = new ConfigurationSchemaErrors();
766 // Get Updated Factory List from File
767 int lineNumber = 0;
768 try {
769 using (Impersonate()) {
770 using (Stream stream = Host.OpenStreamForRead(ConfigStreamInfo.StreamName)) {
771 if (stream != null) {
772 ConfigStreamInfo.HasStream = true;
774 using (XmlUtil xmlUtil = new XmlUtil(stream, ConfigStreamInfo.StreamName, true, errors)) {
775 try {
776 factoryList = ScanFactories(xmlUtil);
777 ThrowIfParseErrors(xmlUtil.SchemaErrors);
779 catch {
780 lineNumber = xmlUtil.LineNumber;
781 throw;
788 // Add implicit sections to the factory list
789 if (factoryList == null) {
790 // But if factoryList isn't found in this config, we still try to
791 // add implicit sections to an empty factoryList.
792 factoryList = new Hashtable();
795 AddImplicitSections(factoryList);
797 if (factoryList != null) {
798 factoryRecord = (FactoryRecord) factoryList[configKey];
802 // Guarantee that exceptions contain the name of the stream and an approximate line number if available.
804 // And don't allow frames up the stack to run exception filters while impersonated.
805 catch (Exception e) {
806 errors.AddError(
807 ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, ConfigStreamInfo.StreamName, lineNumber),
808 ExceptionAction.Global);
812 // Set/Add/Remove Record
813 // Note that the _factoryRecords hashtable is protected by the hierarchy lock.
814 if (factoryRecord != null || HasFactoryRecords) {
815 EnsureFactories()[configKey] = factoryRecord;
818 // Throw accumulated errors
819 ThrowIfParseErrors(errors);
822 internal IInternalConfigHost Host {
823 get {return _configRoot.Host;}
826 internal IInternalConfigurationBuilderHost ConfigBuilderHost {
827 get { return _configRoot.ConfigBuilderHost; }
830 internal BaseConfigurationRecord Parent {
831 get {return _parent;}
834 internal bool IsRootConfig {
835 get {return _parent == null;}
838 internal bool IsMachineConfig {
839 get {return _parent == _configRoot.RootConfigRecord;}
842 internal string LocationSubPath {
843 get {return _locationSubPath;}
846 internal bool IsLocationConfig {
847 get {return _locationSubPath != null;}
850 protected ConfigRecordStreamInfo ConfigStreamInfo {
851 get {
852 if (IsLocationConfig) {
853 return _parent._configStreamInfo;
855 else {
856 return _configStreamInfo;
861 private object GetSection(string configKey, bool getLkg, bool checkPermission) {
862 object result;
863 object resultRuntimeObject;
866 // Note that GetSectionRecursive may invalidate this record,
867 // so there should be no further references to 'this' after the call.
869 GetSectionRecursive(
870 configKey, getLkg, checkPermission, true /* getRuntimeObject */, true /* requestIsHere */,
871 out result, out resultRuntimeObject);
873 return resultRuntimeObject;
876 private void GetSectionRecursive(
877 string configKey, bool getLkg, bool checkPermission, bool getRuntimeObject, bool requestIsHere,
878 out object result, out object resultRuntimeObject) {
880 result = null;
881 resultRuntimeObject = null;
883 #if DBG
884 Debug.Assert(requestIsHere || !checkPermission, "requestIsHere || !checkPermission");
885 if (getLkg) {
886 Debug.Assert(getRuntimeObject == true, "getRuntimeObject == true");
887 Debug.Assert(requestIsHere == true, "requestIsHere == true");
889 #endif
892 // Store results in temporary variables, because we don't want to return
893 // results if an exception is thrown by CheckPermissionAllowed.
895 object tmpResult = null;
896 object tmpResultRuntimeObject = null;
897 bool requirePermission = true;
898 bool isResultTrustedWithoutAptca = true;
900 // Throw errors from initial parse, if any.
901 if (!getLkg) {
902 ThrowIfInitErrors();
906 // check for a cached result
908 bool hasResult = false;
909 SectionRecord sectionRecord = GetSectionRecord(configKey, getLkg);
910 if (sectionRecord != null && sectionRecord.HasResult) {
911 // Results should never be stored if the section has errors.
912 Debug.Assert(!sectionRecord.HasErrors, "!sectionRecord.HasErrors");
914 // Create the runtime object if requested and does not yet exist.
915 if (getRuntimeObject && !sectionRecord.HasResultRuntimeObject) {
916 try {
917 sectionRecord.ResultRuntimeObject = GetRuntimeObject(sectionRecord.Result);
919 catch {
921 // Ignore the error if we are attempting to retreive
922 // the last known good configuration.
924 if (!getLkg) {
925 throw;
930 // Get the cached result.
931 if (!getRuntimeObject || sectionRecord.HasResultRuntimeObject) {
932 requirePermission = sectionRecord.RequirePermission;
933 isResultTrustedWithoutAptca = sectionRecord.IsResultTrustedWithoutAptca;
934 tmpResult = sectionRecord.Result;
935 if (getRuntimeObject) {
936 tmpResultRuntimeObject = sectionRecord.ResultRuntimeObject;
939 hasResult = true;
944 // If there is no cached result, get the parent's section,
945 // then merge it with our own input if we have any.
947 if (!hasResult) {
948 FactoryRecord factoryRecord = null;
949 bool hasInput = (sectionRecord != null && sectionRecord.HasInput);
952 // We want to cache results in a section record if:
953 // - The request is made at this level, and so is likely to be
954 // made here again.
955 // OR
956 // - The section has input, in which case we want to
957 // avoid evaluating the same input multiple times.
959 bool cacheResults = (requestIsHere || hasInput);
961 bool isRootDeclaration;
962 try {
964 // We need to get a factory record to:
965 // - Check whether the caller has permission to access a section.
966 // - Determine if this is the root declaration of a config section,
967 // and thus the termination point for recursion.
968 // - Get a factory that can create a configuration section.
970 // Since most factories will be declared in machine.config and not in
971 // child config files, we do not optimize for checking whether a
972 // factory record is the root declaration, as the calculation at
973 // machine.config is trivial.
975 // It WILL be common in web scenarios for there to be
976 // deep hierarchies of config files, most of which have
977 // sparse input. Therefore we do not want to retreive a
978 // factory record if it is not necessary to do so, as
979 // it would always lead to an order N-squared operation,
980 // where N is the depth of the config hierarchy.
982 // We can skip the reteival of a factory record if:
983 // - This is the recursive call to GetSectionRecursive,
984 // AND
985 // - There is no section input at this level,
986 // AND
987 // - No factory is declared at this level.
989 // In this case, we'll simply continue the recursion to our parent.
991 if (requestIsHere) {
993 // Ensure that we have a valid factory record and a valid factory
994 // for creating sections when a request for a section is first
995 // made.
997 factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
1000 // If initialization is delayed, complete initialization if:
1001 // - We can't find the requested factory, and it therefore
1002 // may be in the file we haven't yet read,
1003 // OR
1004 // - The definition of that factory is allowed at
1005 // levels of the config hierarchy that have not
1006 // been initialized.
1008 // This works for client config scenarios because the default
1009 // for AllowExeDefinition is MachineToApplication. It would not
1010 // be useful for Web scenarios, as most sections can be requested
1011 // Everywhere.
1013 // Note that configuration errors that may be present in the
1014 // file where intialization is delayed will be ignored, and
1015 // thus the order in which configuration sections are requested
1016 // will affect results. This is considered OK as it is very
1017 // expensive to determine configuration paths to
1018 // client user configuration files, which aren't needed by
1019 // most applications.
1021 if ( IsInitDelayed
1022 && ( factoryRecord == null
1023 || _initDelayedRoot.IsDefinitionAllowed(factoryRecord.AllowDefinition, factoryRecord.AllowExeDefinition))) {
1026 // We are going to remove this record, so get any data we need
1027 // before the reference to 'this' becomes invalid.
1029 string configPath = this._configPath;
1030 InternalConfigRoot configRoot = this._configRoot;
1032 // Tell the host to no longer permit delayed initialization.
1033 Host.RequireCompleteInit(_initDelayedRoot);
1035 // Removed config at the root of where initialization is delayed.
1036 _initDelayedRoot.Remove();
1038 // Get the config record for this config path
1039 BaseConfigurationRecord newRecord = (BaseConfigurationRecord) configRoot.GetConfigRecord(configPath);
1041 // Repeat the call to GetSectionRecursive
1042 newRecord.GetSectionRecursive(
1043 configKey, getLkg, checkPermission,
1044 getRuntimeObject, requestIsHere,
1045 out result, out resultRuntimeObject);
1047 // Return and make no more references to this record.
1048 return;
1052 // For compatibility with previous versions,
1053 // return null if the section is not found
1054 // or is a group.
1056 if (factoryRecord == null || factoryRecord.IsGroup) {
1057 return;
1061 // Use the factory record's copy of the configKey,
1062 // so that we don't store more than one instance
1063 // of the same configKey.
1065 configKey = factoryRecord.ConfigKey;
1067 else if (hasInput) {
1069 // We'll need a factory to evaluate the input.
1071 factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
1072 Debug.Assert(factoryRecord != null, "factoryRecord != null");
1074 else {
1076 // We don't need a factory record unless this is the root declaration.
1077 // We know it is not the root declaration if there is no factory
1078 // declared here. This is important to avoid a walk up the config
1079 // hierachy when there is no input in this record.
1081 factoryRecord = GetFactoryRecord(configKey, false);
1082 if (factoryRecord == null) {
1083 isRootDeclaration = false;
1085 else {
1086 factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
1087 Debug.Assert(factoryRecord != null, "factoryRecord != null");
1091 // We need a factory record to check permission.
1092 Debug.Assert(!checkPermission || factoryRecord != null, "!checkPermission || factoryRecord != null");
1095 // If this is the root declaration, then we always want to cache
1096 // the result, in order to prevent the section default from being
1097 // created multiple times.
1099 if (isRootDeclaration) {
1100 cacheResults = true;
1104 // We'll need a section record to cache results,
1105 // and maybe to use in creating the section default.
1107 if (sectionRecord == null && cacheResults) {
1108 sectionRecord = EnsureSectionRecord(configKey, true);
1112 // Retrieve the parent's runtime object if the runtimeObject
1113 // is requested, and we are not going to merge that input
1114 // with input in this section.
1116 bool getParentRuntimeObject = (getRuntimeObject && !hasInput);
1118 object parentResult = null;
1119 object parentResultRuntimeObject = null;
1120 if (isRootDeclaration) {
1122 // Create the default section.
1124 // Use the existing section record to create it if there is no input,
1125 // so that the cached result is attached to the correct record.
1127 SectionRecord sectionRecordForDefault = (hasInput) ? null : sectionRecord;
1128 CreateSectionDefault(configKey, getParentRuntimeObject, factoryRecord, sectionRecordForDefault,
1129 out parentResult, out parentResultRuntimeObject);
1131 else {
1133 // Get the parent section.
1135 _parent.GetSectionRecursive(
1136 configKey, false /* getLkg */, false /* checkPermission */,
1137 getParentRuntimeObject, false /* requestIsHere */,
1138 out parentResult, out parentResultRuntimeObject);
1141 if (hasInput) {
1143 // Evaluate the input.
1145 // If Evaluate() encounters an error, it may not throw an exception
1146 // when getLkg == true.
1148 // The complete success of the evaluation is determined by the return value.
1150 bool success = Evaluate(factoryRecord, sectionRecord, parentResult, getLkg, getRuntimeObject,
1151 out tmpResult, out tmpResultRuntimeObject);
1153 Debug.Assert(success || getLkg, "success || getLkg");
1155 if (!success) {
1156 Debug.Assert(getLkg == true, "getLkg == true");
1157 // Do not cache partial results if getLkg was specified.
1158 cacheResults = false;
1161 else {
1163 // If we are going to cache results here, we will need
1164 // to create a copy in the case of MgmtConfigurationRecord -
1165 // otherwise we could inadvertently return the parent to the user,
1166 // which could then be modified.
1168 if (sectionRecord != null) {
1169 tmpResult = UseParentResult(configKey, parentResult, sectionRecord);
1170 if (getRuntimeObject) {
1172 // If the parent result is the same as the parent runtime object,
1173 // then use the same copy of the parent result for our own runtime object.
1175 if (object.ReferenceEquals(parentResult, parentResultRuntimeObject)) {
1176 tmpResultRuntimeObject = tmpResult;
1178 else {
1179 tmpResultRuntimeObject = UseParentResult(configKey, parentResultRuntimeObject, sectionRecord);
1183 else {
1184 Debug.Assert(!requestIsHere, "!requestIsHere");
1187 // We don't need to make a copy if we are not storing
1188 // the result, and thus not returning the result to the
1189 // caller of GetSection.
1191 tmpResult = parentResult;
1192 tmpResultRuntimeObject = parentResultRuntimeObject;
1197 // Determine which permissions are required of the caller.
1199 if (cacheResults || checkPermission) {
1200 requirePermission = factoryRecord.RequirePermission;
1201 isResultTrustedWithoutAptca = factoryRecord.IsFactoryTrustedWithoutAptca;
1204 // Cache the results.
1206 if (cacheResults) {
1207 if (sectionRecord == null) {
1208 sectionRecord = EnsureSectionRecord(configKey, true);
1211 sectionRecord.Result = tmpResult;
1212 if (getRuntimeObject) {
1213 sectionRecord.ResultRuntimeObject = tmpResultRuntimeObject;
1216 sectionRecord.RequirePermission = requirePermission;
1217 sectionRecord.IsResultTrustedWithoutAptca = isResultTrustedWithoutAptca;
1221 hasResult = true;
1223 catch {
1225 // Ignore the error if we are attempting to retreive
1226 // the last known good configuration.
1228 if (!getLkg) {
1229 throw;
1234 // If we don't have a result, ask our parent for its
1235 // last known good result.
1237 if (!hasResult) {
1238 Debug.Assert(getLkg == true, "getLkg == true");
1240 _parent.GetSectionRecursive(
1241 configKey, true /* getLkg */, checkPermission,
1242 true /* getRuntimeObject */, true /* requestIsHere */,
1243 out result, out resultRuntimeObject);
1245 return;
1250 // Check if permission to access the section is allowed.
1252 if (checkPermission) {
1253 CheckPermissionAllowed(configKey, requirePermission, isResultTrustedWithoutAptca);
1257 // Return the results.
1259 result = tmpResult;
1260 if (getRuntimeObject) {
1261 resultRuntimeObject = tmpResultRuntimeObject;
1265 protected void CreateSectionDefault(
1266 string configKey, bool getRuntimeObject, FactoryRecord factoryRecord, SectionRecord sectionRecord,
1267 out object result, out object resultRuntimeObject) {
1269 result = null;
1270 resultRuntimeObject = null;
1272 SectionRecord sectionRecordForDefault;
1273 if (sectionRecord != null) {
1274 sectionRecordForDefault = sectionRecord;
1276 else {
1277 sectionRecordForDefault = new SectionRecord(configKey);
1280 object tmpResult = CallCreateSection(true, factoryRecord, sectionRecordForDefault, null, null, null);
1281 object tmpResultRuntimeObject;
1282 if (getRuntimeObject) {
1283 tmpResultRuntimeObject = GetRuntimeObject(tmpResult);
1285 else {
1286 tmpResultRuntimeObject = null;
1289 result = tmpResult;
1290 resultRuntimeObject = tmpResultRuntimeObject;
1293 #if UNUSED_CODE
1295 // Create a section that is used to manipulate a section that has errors.
1297 private DefaultSection CreateErrorSection(
1298 string configKey, FactoryRecord factoryRecord, SectionRecord sectionRecord, SectionInput input, Exception e) {
1301 // We make assumptions in this implemntation that CreateErrorSection is called
1302 // only for MgmtConfigurationRecord.
1304 Debug.Assert(this is MgmtConfigurationRecord, "this is MgmtConfigurationRecord");
1306 string group;
1307 string name;
1308 string factoryTypeName;
1309 bool allowLocation;
1310 ConfigurationAllowDefinition allowDefinition;
1311 ConfigurationAllowExeDefinition allowExeDefinition;
1312 bool restartOnExternalChanges;
1313 bool requirePermission;
1314 bool isFromTrustedConfigRecord;
1315 string factoryFilename;
1316 int factoryLineNumber;
1318 // The original factory record will contain either valid values,
1319 // or the default values in places where there was an error.
1320 if (factoryRecord != null) {
1321 configKey = factoryRecord.ConfigKey;
1322 group = factoryRecord.Group;
1323 name = factoryRecord.Name;
1324 factoryTypeName = factoryRecord.FactoryTypeName;
1325 allowLocation = factoryRecord.AllowLocation;
1326 allowDefinition = factoryRecord.AllowDefinition;
1327 allowExeDefinition = factoryRecord.AllowExeDefinition;
1328 restartOnExternalChanges = factoryRecord.RestartOnExternalChanges;
1329 requirePermission = factoryRecord.RequirePermission;
1330 isFromTrustedConfigRecord = factoryRecord.IsFromTrustedConfigRecord;
1331 factoryFilename = factoryRecord.Filename;
1332 factoryLineNumber = factoryRecord.LineNumber;
1334 else {
1335 SplitConfigKey(configKey, out group, out name);
1336 factoryTypeName = typeof(DefaultSection).AssemblyQualifiedName;
1337 allowLocation = true;
1338 allowDefinition = ConfigurationAllowDefinition.Everywhere;
1339 allowExeDefinition = ConfigurationAllowExeDefinition.MachineToApplication;
1340 restartOnExternalChanges = true;
1341 requirePermission = true;
1342 isFromTrustedConfigRecord = false;
1343 factoryFilename = null;
1344 factoryLineNumber = -1;
1347 // create a new factory record
1348 FactoryRecord factoryRecordForCreate = new FactoryRecord(
1349 configKey,
1350 group,
1351 name,
1352 factoryTypeName,
1353 allowLocation,
1354 allowDefinition,
1355 allowExeDefinition,
1356 restartOnExternalChanges,
1357 requirePermission,
1358 isFromTrustedConfigRecord,
1359 factoryFilename,
1360 factoryLineNumber);
1363 // Create the factory needed to create the section.
1365 Debug.Assert(this is MgmtConfigurationRecord, "this is MgmtConfigurationRecord");
1366 ConstructorInfo ctor = TypeUtil.GetConstructorWithReflectionPermission(typeof(DefaultSection), typeof(ConfigurationSection), true);
1367 factoryRecordForCreate.Factory = ctor;
1368 factoryRecordForCreate.IsFactoryTrustedWithoutAptca = false;
1371 // Try to get the section content as raw xml.
1373 ConfigXmlReader configXmlReader = null;
1374 string filename = null;
1375 int lineNumber = -1;
1376 if (input != null) {
1377 filename = input.SectionXmlInfo.Filename;
1378 lineNumber = input.SectionXmlInfo.LineNumber;
1379 string [] keys = sectionRecord.ConfigKey.Split(ConfigPathSeparatorParams);
1380 try {
1381 configXmlReader = GetSectionXmlReader(keys, input);
1383 catch {
1387 DefaultSection section = (DefaultSection) CallCreateSection(
1388 isFromTrustedConfigRecord, factoryRecordForCreate, sectionRecord,
1389 null /* parentConfig */, configXmlReader, filename, lineNumber);
1391 section.ElementInformation.SetError(configKey, e);
1393 return section;
1395 #endif
1398 // Check if an input should be skipped based on inheritInChildApplications setting.
1400 // If skipInChildApps == true (it means inheritInChildApplications=="false" in the location tag):
1402 // - If _flags[IsAboveApplication]==true, that means the app pointed to by _configPath is above of the
1403 // current running app. In another word, the running app is a child app of the app pointed to by _configPath.
1404 // In this case, we should skip the input.
1406 // - If _flags[IsAboveApplication]==false, that means the app pointed to by _configPath == current running app.
1407 // In this case it's okay to use the input.
1409 private bool ShouldSkipDueToInheritInChildApplications(bool skipInChildApps) {
1410 return (skipInChildApps && _flags[IsAboveApplication]);
1413 // configPath - The config path of the config record to which a location tag points to.
1414 private bool ShouldSkipDueToInheritInChildApplications(bool skipInChildApps, string configPath) {
1415 return (skipInChildApps && Host.IsAboveApplication(configPath));
1420 // Evaluate the input.
1422 // If Evaluate() encounters an error, it may not throw an exception
1423 // when getLkg == true.
1425 // The complete success of the evaluation is determined by the return value.
1427 private bool Evaluate(
1428 FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentResult,
1429 bool getLkg, bool getRuntimeObject, out object result, out object resultRuntimeObject) {
1431 result = null;
1432 resultRuntimeObject = null;
1435 // Store results in temporary variables, because we don't want to return
1436 // results if an exception is thrown.
1438 object tmpResult = null;
1439 object tmpResultRuntimeObject = null;
1441 // Factory record should be error-free.
1442 Debug.Assert(!factoryRecord.HasErrors, "!factoryRecord.HasErrors");
1444 // We should have some input
1445 Debug.Assert(sectionRecord.HasInput, "sectionRecord.HasInput");
1448 // Grab inputs before checking result,
1449 // as inputs may be cleared once the result is set.
1451 List<SectionInput> locationInputs = sectionRecord.LocationInputs;
1452 List<SectionInput> indirectLocationInputs = sectionRecord.IndirectLocationInputs;
1453 SectionInput fileInput = sectionRecord.FileInput;
1455 Debug.Assert(!(IsLocationConfig && getLkg),
1456 "Don't expect getLkg to be true when we're dealing with a location config record");
1459 // Now that we have our inputs, lets check if there
1460 // is a result, because if there is, our inputs might
1461 // have been invalidated.
1463 bool success = false;
1464 if (sectionRecord.HasResult) {
1465 // Results should never be stored if the section has errors.
1466 Debug.Assert(!sectionRecord.HasErrors, "!sectionRecord.HasErrors");
1468 // Create the runtime object if requested and it does not yet exist.
1469 if (getRuntimeObject && !sectionRecord.HasResultRuntimeObject) {
1470 try {
1471 sectionRecord.ResultRuntimeObject = GetRuntimeObject(sectionRecord.Result);
1473 catch {
1475 // Ignore the error if we are attempting to retreive
1476 // the last known good configuration.
1478 if (!getLkg) {
1479 throw;
1484 // Get the cached result.
1485 if (!getRuntimeObject || sectionRecord.HasResultRuntimeObject) {
1486 tmpResult = sectionRecord.Result;
1487 if (getRuntimeObject) {
1488 tmpResultRuntimeObject = sectionRecord.ResultRuntimeObject;
1491 success = true;
1495 if (!success) {
1496 Exception savedException = null;
1497 try {
1498 string configKey = factoryRecord.ConfigKey;
1499 string [] keys = configKey.Split(ConfigPathSeparatorParams);
1500 object currentResult = parentResult;
1503 // Evaluate indirectLocationInputs. Used only in location config record.
1504 // See the comment for VSWhidbey 540184 in Init() for more details.
1506 if (indirectLocationInputs != null) {
1507 Debug.Assert(IsLocationConfig, "indirectLocationInputs is non-null only in location config record");
1508 foreach (SectionInput input in indirectLocationInputs) {
1509 if (!input.HasResult) {
1510 input.ThrowOnErrors();
1511 bool isTrusted = Host.IsTrustedConfigPath(input.SectionXmlInfo.DefinitionConfigPath);
1512 input.Result = EvaluateOne(keys, input, isTrusted, factoryRecord, sectionRecord, currentResult);
1515 currentResult = input.Result;
1520 // Evaluate location inputs
1522 if (locationInputs != null) {
1523 foreach (SectionInput locationInput in locationInputs) {
1524 if (!locationInput.HasResult) {
1525 locationInput.ThrowOnErrors();
1526 bool isTrusted = Host.IsTrustedConfigPath(locationInput.SectionXmlInfo.DefinitionConfigPath);
1527 locationInput.Result = EvaluateOne(keys, locationInput, isTrusted, factoryRecord, sectionRecord, currentResult);
1530 currentResult = locationInput.Result;
1535 // Evaluate file input
1537 if (fileInput != null) {
1538 if (!fileInput.HasResult) {
1539 fileInput.ThrowOnErrors();
1540 bool isTrusted = _flags[IsTrusted];
1541 fileInput.Result = EvaluateOne(keys, fileInput, isTrusted, factoryRecord, sectionRecord, currentResult);
1544 currentResult = fileInput.Result;
1546 else {
1548 // The section needs its own copy of the result that is distinct
1549 // from its location parent result.
1551 // Please note that this is needed only in design-time because any
1552 // change to the section shouldn't go to the locationInput.Result.
1554 // In runtime, UseParentResult does nothing.
1556 Debug.Assert(locationInputs != null || indirectLocationInputs != null,
1557 "locationInputs != null || indirectLocationInputs != null");
1558 currentResult = UseParentResult(configKey, currentResult, sectionRecord);
1561 if (getRuntimeObject) {
1562 tmpResultRuntimeObject = GetRuntimeObject(currentResult);
1565 tmpResult = currentResult;
1566 success = true;
1568 catch (Exception e) {
1570 // Catch the exception if LKG is requested and we have
1571 // location input to fall back on.
1573 if (getLkg && locationInputs != null) {
1574 savedException = e;
1576 else {
1577 throw;
1583 // If getLkg, then return a result from the last valid location input.
1585 if (!success) {
1586 Debug.Assert(getLkg == true, "getLkg == true");
1588 int i = locationInputs.Count;
1589 while (--i >= 0) {
1590 SectionInput locationInput = locationInputs[i];
1591 if (locationInput.HasResult) {
1592 if (getRuntimeObject && !locationInput.HasResultRuntimeObject) {
1593 try {
1594 locationInput.ResultRuntimeObject = GetRuntimeObject(locationInput.Result);
1596 catch {
1600 if (!getRuntimeObject || locationInput.HasResultRuntimeObject) {
1601 tmpResult = locationInput.Result;
1602 if (getRuntimeObject) {
1603 tmpResultRuntimeObject = locationInput.ResultRuntimeObject;
1606 break;
1611 if (i < 0) {
1612 throw savedException;
1618 // If evaluation was successful, we can remove any saved rawXml.
1620 if (success && !_flags[SupportsKeepInputs]) {
1621 sectionRecord.ClearRawXml();
1624 result = tmpResult;
1625 if (getRuntimeObject) {
1626 resultRuntimeObject = tmpResultRuntimeObject;
1629 return success;
1632 private object EvaluateOne(
1633 string[] keys, SectionInput input, bool isTrusted,
1634 FactoryRecord factoryRecord, SectionRecord sectionRecord, object parentResult) {
1636 object result;
1637 try {
1638 ConfigXmlReader reader = GetSectionXmlReader(keys, input);
1639 if (reader == null) {
1641 // If section is not found in a file, use the parent result
1643 result = UseParentResult(factoryRecord.ConfigKey, parentResult, sectionRecord);
1645 else {
1646 result = CallCreateSection(isTrusted, factoryRecord, sectionRecord, input, parentResult, reader);
1649 catch (Exception e) {
1650 throw ExceptionUtil.WrapAsConfigException(
1651 SR.GetString(SR.Config_exception_creating_section, factoryRecord.ConfigKey),
1652 e, input.SectionXmlInfo);
1655 return result;
1659 // Create a single cached instance of UnrestrictedConfigPermission.
1661 private static ConfigurationPermission UnrestrictedConfigPermission {
1662 get {
1663 if (s_unrestrictedConfigPermission == null) {
1664 s_unrestrictedConfigPermission = new ConfigurationPermission(PermissionState.Unrestricted);
1667 return s_unrestrictedConfigPermission;
1672 // Check whether permission to the section is allowed to the caller.
1674 private void CheckPermissionAllowed(string configKey, bool requirePermission, bool isTrustedWithoutAptca) {
1676 // Demand unrestricted ConfigurationPermission if the section requires it
1678 if (requirePermission) {
1680 try {
1681 UnrestrictedConfigPermission.Demand();
1683 catch (SecurityException e) {
1685 // Add a nice error message that includes the sectionName and explains
1686 // how to use the requirePermission attribute.
1688 throw new SecurityException(
1689 SR.GetString(SR.ConfigurationPermission_Denied, configKey),
1695 // Ensure that the recepient isn't receiving an object they otherwise
1696 // wouldn't be able to create due to Aptca.
1698 if (isTrustedWithoutAptca && !Host.IsFullTrustSectionWithoutAptcaAllowed(this)) {
1699 throw new ConfigurationErrorsException(SR.GetString(SR.Section_from_untrusted_assembly, configKey));
1704 private ConfigXmlReader FindSection(string [] keys, SectionXmlInfo sectionXmlInfo, out int lineNumber) {
1705 lineNumber = 0;
1706 ConfigXmlReader section = null;
1707 try {
1708 using (Impersonate()) {
1709 using (Stream stream = Host.OpenStreamForRead(sectionXmlInfo.Filename)) {
1710 if ( !_flags[SupportsRefresh]
1711 && (stream == null || HasStreamChanged(sectionXmlInfo.Filename, sectionXmlInfo.StreamVersion))) {
1713 throw new ConfigurationErrorsException(SR.GetString(SR.Config_file_has_changed), sectionXmlInfo.Filename, 0);
1716 if (stream != null) {
1718 using (XmlUtil xmlUtil = new XmlUtil(stream, sectionXmlInfo.Filename, true)) {
1719 if (sectionXmlInfo.SubPath == null) {
1720 section = FindSectionRecursive(keys, 0, xmlUtil, ref lineNumber);
1722 else {
1723 // search children of <configuration> for <location>
1724 xmlUtil.ReadToNextElement();
1725 while (xmlUtil.Reader.Depth > 0) {
1726 if (xmlUtil.Reader.Name == KEYWORD_LOCATION) {
1727 bool locationValid = false;
1728 string locationSubPathAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_LOCATION_PATH);
1730 try {
1731 locationSubPathAttribute = NormalizeLocationSubPath(locationSubPathAttribute, xmlUtil);
1732 locationValid = true;
1734 catch (ConfigurationException ce) {
1735 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.NonSpecific);
1738 if (locationValid &&
1739 StringUtil.EqualsIgnoreCase(sectionXmlInfo.SubPath, locationSubPathAttribute)) {
1741 section = FindSectionRecursive(keys, 0, xmlUtil, ref lineNumber);
1742 if (section != null)
1743 break;
1747 xmlUtil.SkipToNextElement();
1751 // Throw accumulated errors
1752 ThrowIfParseErrors(xmlUtil.SchemaErrors);
1758 // Don't allow frames up the stack to run exception filters while impersonated.
1759 catch {
1760 throw;
1763 return section;
1766 private ConfigXmlReader FindSectionRecursive(string [] keys, int iKey, XmlUtil xmlUtil, ref int lineNumber) {
1767 string name = keys[iKey];
1768 ConfigXmlReader section = null;
1770 int depth = xmlUtil.Reader.Depth;
1771 xmlUtil.ReadToNextElement();
1773 while (xmlUtil.Reader.Depth > depth) {
1774 if (xmlUtil.Reader.Name == name) {
1775 if (iKey < keys.Length - 1) {
1777 // We haven't reached the section yet, so keep evaluating
1779 section = FindSectionRecursive(keys, iKey + 1, xmlUtil, ref lineNumber);
1780 if (section != null) {
1781 break;
1784 continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
1786 else {
1788 // We've reached the section. Load the section into a string.
1790 string filename = ((IConfigErrorInfo)xmlUtil).Filename;
1791 int lineOffset = xmlUtil.Reader.LineNumber;
1792 string rawXml = xmlUtil.CopySection();
1793 section = new ConfigXmlReader(rawXml, filename, lineOffset);
1794 break;
1797 else if (iKey == 0 && xmlUtil.Reader.Name == KEYWORD_LOCATION) {
1798 string locationSubPath = xmlUtil.Reader.GetAttribute(KEYWORD_LOCATION_PATH);
1799 bool isValid = false;
1800 try {
1801 locationSubPath = NormalizeLocationSubPath(locationSubPath, xmlUtil);
1802 isValid = true;
1804 catch (ConfigurationException ce) {
1805 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.NonSpecific);
1808 if (isValid && locationSubPath == null) {
1810 // Location sections that don't have a subpath are treated
1811 // as ordinary sections.
1813 section = FindSectionRecursive(keys, iKey, xmlUtil, ref lineNumber);
1814 if (section != null) {
1815 break;
1818 continue; // don't call "Skip" -- FindSectionRecursive forwards the reader
1822 xmlUtil.SkipToNextElement();
1825 return section;
1828 private ConfigXmlReader LoadConfigSource(string name, SectionXmlInfo sectionXmlInfo) {
1829 string configSourceStreamName = sectionXmlInfo.ConfigSourceStreamName;
1831 try {
1832 using (Impersonate()) {
1833 using (Stream stream = Host.OpenStreamForRead(configSourceStreamName)) {
1834 if (stream == null) {
1835 throw new ConfigurationErrorsException(
1836 SR.GetString(SR.Config_cannot_open_config_source, sectionXmlInfo.ConfigSource),
1837 sectionXmlInfo);
1840 using (XmlUtil xmlUtil = new XmlUtil(stream, configSourceStreamName, true)) {
1841 if (xmlUtil.Reader.Name != name) {
1842 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_file_format), xmlUtil);
1845 // Check for protectionProvider
1846 string protectionProviderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_PROTECTION_PROVIDER);
1847 if (protectionProviderAttribute != null) {
1848 if (xmlUtil.Reader.AttributeCount != 1) {
1849 // Error: elements with protectionProvider should not have other attributes
1850 throw new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_syntax_error), xmlUtil);
1853 sectionXmlInfo.ProtectionProviderName = ValidateProtectionProviderAttribute(protectionProviderAttribute, xmlUtil);
1856 // Check for configBuilder
1857 string configBuilderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_CONFIG_BUILDER);
1858 if (configBuilderAttribute != null) {
1859 sectionXmlInfo.ConfigBuilderName = ValidateConfigBuilderAttribute(configBuilderAttribute, xmlUtil);
1862 int lineOffset = xmlUtil.Reader.LineNumber;
1863 string rawXml = xmlUtil.CopySection();
1865 // Detect if there is any XML left over after the section
1866 while (!xmlUtil.Reader.EOF) {
1867 XmlNodeType t = xmlUtil.Reader.NodeType;
1868 if (t != XmlNodeType.Comment) {
1869 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_file_format), xmlUtil);
1872 xmlUtil.Reader.Read();
1875 ConfigXmlReader section = new ConfigXmlReader(rawXml, configSourceStreamName, lineOffset);
1876 return section;
1881 catch {
1882 // Don't allow frames up the stack to run exception filters while impersonated.
1883 throw;
1887 protected ConfigXmlReader GetSectionXmlReader(string[] keys, SectionInput input) {
1888 ConfigXmlReader reader = null;
1889 string filename = input.SectionXmlInfo.Filename;
1890 int lineNumber = input.SectionXmlInfo.LineNumber;
1892 try {
1893 string name = keys[keys.Length-1];
1894 string rawXml = input.SectionXmlInfo.RawXml;
1895 if (rawXml != null) {
1896 // Use the stored raw xml to provide the content of the section.
1897 reader = new ConfigXmlReader(rawXml, input.SectionXmlInfo.Filename, input.SectionXmlInfo.LineNumber);
1899 else if (!String.IsNullOrEmpty(input.SectionXmlInfo.ConfigSource)) {
1900 // Load the config source to provide the content of the section.
1901 filename = input.SectionXmlInfo.ConfigSourceStreamName;
1902 lineNumber = 0;
1903 reader = LoadConfigSource(name, input.SectionXmlInfo);
1905 else {
1906 // Find the content of the section in the config file.
1907 lineNumber = 0;
1908 reader = FindSection(keys, input.SectionXmlInfo, out lineNumber);
1911 // Decrypt protected sections
1912 if (reader != null) {
1913 if (!input.IsProtectionProviderDetermined) {
1914 input.ProtectionProvider = GetProtectionProviderFromName(input.SectionXmlInfo.ProtectionProviderName, false);
1917 if (input.ProtectionProvider != null) {
1918 reader = DecryptConfigSection(reader, input.ProtectionProvider);
1922 // Allow configBuilder a chance to modify
1923 if (reader != null) {
1924 if (!input.IsConfigBuilderDetermined && !String.IsNullOrWhiteSpace(input.SectionXmlInfo.ConfigBuilderName)) {
1925 input.ConfigBuilder = GetConfigBuilderFromName(input.SectionXmlInfo.ConfigBuilderName);
1928 if (input.IsConfigBuilderDetermined) {
1929 // Decrypt removes the invalid "configProtectionProvider" attribute simply by returning an xml reader for the
1930 // decrypted inner xml... which persumably does not have the attribute. ConfigBuilders OTOH, will presumably
1931 // return an xml reader based on this whole section as is. And the "configBuilders" attribute will be an
1932 // "unrecognized attribute" further down the road. So let's remove it here.
1933 XmlDocument doc = new XmlDocument();
1934 doc.PreserveWhitespace = true;
1935 doc.LoadXml(reader.RawXml);
1936 doc.DocumentElement.RemoveAttribute(KEYWORD_CONFIG_BUILDER);
1937 reader = new ConfigXmlReader(doc.DocumentElement.OuterXml, filename, lineNumber);
1940 if (input.ConfigBuilder != null) {
1941 reader = ProcessRawXml(reader, input.ConfigBuilder);
1947 // Guarantee that exceptions contain the name of the stream and an approximate line number.
1949 catch (Exception e) {
1950 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_error_loading_XML_file), e, filename, lineNumber);
1953 return reader;
1956 internal string DefaultProviderName {
1957 get {
1958 return ProtectedConfig.DefaultProvider;
1962 internal ProtectedConfigurationProvider GetProtectionProviderFromName(string providerName, bool throwIfNotFound) {
1963 ProtectedConfigurationProvider provider = null;
1965 if (String.IsNullOrEmpty(providerName)) {
1966 if (throwIfNotFound) {
1967 throw new ConfigurationErrorsException(SR.GetString(SR.ProtectedConfigurationProvider_not_found, providerName));
1969 else {
1970 return null;
1974 provider = ProtectedConfig.GetProviderFromName(providerName);
1976 return provider;
1979 private ProtectedConfigurationSection ProtectedConfig {
1980 get {
1981 if (!_flags[ProtectedDataInitialized]) {
1982 InitProtectedConfigurationSection();
1985 return _protectedConfig;
1989 internal void InitProtectedConfigurationSection() {
1990 if (!_flags[ProtectedDataInitialized]) {
1991 _protectedConfig = GetSection(BaseConfigurationRecord.RESERVED_SECTION_PROTECTED_CONFIGURATION, false, false) as ProtectedConfigurationSection;
1993 Debug.Assert(_protectedConfig != null, "<configProtectedData> section should always be available because it's a built-in section");
1995 _flags[ProtectedDataInitialized] = true;
1999 internal ConfigurationBuilder GetConfigBuilderFromName(string builderName) {
2000 if (String.IsNullOrEmpty(builderName) || ConfigBuilders == null) {
2001 throw new ConfigurationErrorsException(SR.GetString(SR.Config_builder_not_found, builderName));
2004 return ConfigBuilders.GetBuilderFromName(builderName);
2007 private ConfigurationBuildersSection ConfigBuilders {
2008 get {
2009 if (!_flags[ConfigBuildersInitialized]) {
2010 InitConfigBuildersSection();
2013 return _configBuilders;
2017 internal void InitConfigBuildersSection() {
2018 if (!_flags[ConfigBuildersInitialized]) {
2019 _configBuilders = GetSection(BaseConfigurationRecord.RESERVED_SECTION_CONFIGURATION_BUILDERS, false, false) as ConfigurationBuildersSection;
2021 _flags[ConfigBuildersInitialized] = true;
2025 protected object CallCreateSection(bool inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, SectionInput sectionInput, object parentConfig, ConfigXmlReader reader) {
2026 object config;
2027 string filename = null;
2028 int line = -1;
2030 if (sectionInput != null && sectionInput.SectionXmlInfo != null) {
2031 filename = sectionInput.SectionXmlInfo.Filename;
2032 line = sectionInput.SectionXmlInfo.LineNumber;
2035 // Call into config section while impersonating process or UNC identity
2036 // so that the section could read files from disk if needed
2037 try {
2038 using (Impersonate()) {
2039 config = CreateSection(inputIsTrusted, factoryRecord, sectionRecord, sectionInput, parentConfig, reader);
2040 if (config == null && parentConfig != null) {
2041 throw new ConfigurationErrorsException(SR.GetString(SR.Config_object_is_null), filename, line);
2045 catch (ThreadAbortException) {
2046 throw;
2048 catch (Exception e) {
2049 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), e, filename, line);
2052 return config;
2055 // IsRootDeclaration
2057 // Is this the Root Record of where this configKey is Declared
2059 // If parent is null, or there is not a factory record above
2060 // this one, then it is the root of where it can be declared
2062 // Optionally consider whether implicit sections are to be considered rooted.
2064 internal bool IsRootDeclaration(string configKey, bool implicitIsRooted) {
2065 if (!implicitIsRooted && IsImplicitSection(configKey)) {
2066 return false;
2069 return (_parent.IsRootConfig || _parent.FindFactoryRecord(configKey, true) == null);
2072 // Search the config hierarchy for a FactoryRecord.
2073 // Note that callers should check whether the returned factory has errors.
2074 internal FactoryRecord FindFactoryRecord(string configKey, bool permitErrors, out BaseConfigurationRecord configRecord) {
2075 configRecord = null;
2076 BaseConfigurationRecord tConfigRecord = this;
2078 while (!tConfigRecord.IsRootConfig) {
2079 FactoryRecord factoryRecord = tConfigRecord.GetFactoryRecord(configKey, permitErrors);
2080 if (factoryRecord != null) {
2081 #if DBG
2082 if (IsImplicitSection(configKey) && !factoryRecord.HasErrors) {
2083 Debug.Assert(tConfigRecord._parent.IsRootConfig,
2084 "Implicit section should be found only at the record beneath the root (e.g. machine.config)");
2086 #endif
2088 configRecord = tConfigRecord;
2089 return factoryRecord;
2092 tConfigRecord = tConfigRecord._parent;
2095 return null;
2099 internal FactoryRecord FindFactoryRecord(string configKey, bool permitErrors) {
2100 BaseConfigurationRecord dummy;
2101 return FindFactoryRecord(configKey, permitErrors, out dummy);
2105 // FindAndEnsureFactoryRecord:
2107 // - Find the nearest factory record
2108 // - Determine if it is the root
2109 // - Create the factory in the root if it doesn't exist.
2110 // - Determine if the factory type is from a global assembly without APTCA
2111 // - Copy the factory and IsFactoryTrustedWithoutAptca bit into the child record
2113 private FactoryRecord FindAndEnsureFactoryRecord(string configKey, out bool isRootDeclaredHere) {
2114 isRootDeclaredHere = false;
2116 BaseConfigurationRecord configRecord;
2117 FactoryRecord factoryRecord = FindFactoryRecord(configKey, false, out configRecord);
2118 if (factoryRecord != null && !factoryRecord.IsGroup) {
2120 // Find the root declaration
2122 FactoryRecord rootFactoryRecord = factoryRecord;
2123 BaseConfigurationRecord rootConfigRecord = configRecord;
2125 BaseConfigurationRecord currentConfigRecord = configRecord._parent;
2126 while (!currentConfigRecord.IsRootConfig) {
2127 BaseConfigurationRecord tempConfigRecord;
2128 FactoryRecord tempFactoryRecord = currentConfigRecord.FindFactoryRecord(configKey, false, out tempConfigRecord);
2129 if (tempFactoryRecord == null)
2130 break;
2132 rootFactoryRecord = tempFactoryRecord;
2133 rootConfigRecord = tempConfigRecord;
2135 // continue the search from the parent of the configRecord we found
2136 currentConfigRecord = tempConfigRecord.Parent;
2140 // A child factory record must be equivalent to its parent,
2141 // so if the child has no errors, the parent must also have no errors.
2143 Debug.Assert(!rootFactoryRecord.HasErrors, "!rootFactoryRecord.HasErrors");
2144 if (rootFactoryRecord.Factory == null) {
2145 try {
2147 // Create the factory from the type string, and cache it
2149 object factory = rootConfigRecord.CreateSectionFactory(rootFactoryRecord);
2150 bool isFactoryTrustedWithoutAptca = TypeUtil.IsTypeFromTrustedAssemblyWithoutAptca(factory.GetType());
2151 rootFactoryRecord.Factory = factory;
2152 rootFactoryRecord.IsFactoryTrustedWithoutAptca = isFactoryTrustedWithoutAptca;
2154 catch (Exception e) {
2155 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_exception_creating_section_handler, factoryRecord.ConfigKey), e, factoryRecord);
2159 if (factoryRecord.Factory == null) {
2160 factoryRecord.Factory = rootFactoryRecord.Factory;
2161 factoryRecord.IsFactoryTrustedWithoutAptca = rootFactoryRecord.IsFactoryTrustedWithoutAptca;
2164 isRootDeclaredHere = Object.ReferenceEquals(this, rootConfigRecord);
2167 return factoryRecord;
2170 private Hashtable ScanFactories(XmlUtil xmlUtil) {
2171 Hashtable factoryList;
2173 factoryList = new Hashtable();
2175 if (xmlUtil.Reader.NodeType != XmlNodeType.Element || xmlUtil.Reader.Name != KEYWORD_CONFIGURATION) {
2177 string safeFilename = ConfigurationErrorsException.AlwaysSafeFilename(((IConfigErrorInfo)xmlUtil).Filename);
2179 throw new ConfigurationErrorsException(
2180 SR.GetString(SR.Config_file_doesnt_have_root_configuration, safeFilename),
2181 xmlUtil);
2184 // Ignore xmlns attribute
2185 while (xmlUtil.Reader.MoveToNextAttribute()) {
2186 switch (xmlUtil.Reader.Name) {
2187 case KEYWORD_XMLNS:
2188 if (xmlUtil.Reader.Value == KEYWORD_CONFIGURATION_NAMESPACE) {
2189 _flags[NamespacePresentInFile] = true;
2190 _flags[NamespacePresentCurrent] = true;
2191 } else {
2192 ConfigurationErrorsException ce;
2194 ce = new ConfigurationErrorsException(
2195 SR.GetString(SR.Config_namespace_invalid, xmlUtil.Reader.Value, KEYWORD_CONFIGURATION_NAMESPACE),
2196 xmlUtil);
2198 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Global);
2201 break;
2203 default:
2204 xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific);
2205 break;
2209 // move to first child of <configuration>
2210 xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
2211 if (xmlUtil.Reader.Depth == 1 && xmlUtil.Reader.Name == KEYWORD_CONFIGSECTIONS) {
2212 xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific);
2213 ScanFactoriesRecursive(xmlUtil, string.Empty, factoryList);
2216 return factoryList;
2220 // Scans the <configSections> section of a configuration file. The function is recursive
2221 // to traverse arbitrarily nested config groups.
2223 // <sectionGroup name="foo">
2224 // <sectionGroup name="bar">
2225 // <section name="----Section" type="..." />
2226 // ...
2228 // Note: This function valiates that the factory record has not been
2229 // declared before in a parent record. (it does not check
2230 // current record, which allows you to update list)
2232 private void ScanFactoriesRecursive(XmlUtil xmlUtil, string parentConfigKey, Hashtable factoryList) {
2234 // discard any accumulated local errors
2235 xmlUtil.SchemaErrors.ResetLocalErrors();
2237 int depth = xmlUtil.Reader.Depth;
2238 xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
2240 while (xmlUtil.Reader.Depth == depth + 1) {
2241 bool positionedAtNextElement = false;
2243 switch (xmlUtil.Reader.Name) {
2245 // Handle <sectionGroup name="groupName" [type="typename"] />
2247 case KEYWORD_SECTIONGROUP: {
2248 string tagName = null;
2249 string typeName = null;
2251 int lineNumber = xmlUtil.Reader.LineNumber;
2252 while (xmlUtil.Reader.MoveToNextAttribute()) {
2253 switch (xmlUtil.Reader.Name) {
2254 case KEYWORD_SECTIONGROUP_NAME:
2255 tagName = xmlUtil.Reader.Value;
2256 VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false, false);
2257 break;
2259 case KEYWORD_SECTIONGROUP_TYPE:
2260 xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
2261 break;
2263 default:
2264 xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
2265 break;
2268 xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
2270 if (!xmlUtil.VerifyRequiredAttribute(
2271 tagName,
2272 KEYWORD_SECTIONGROUP_NAME,
2273 ExceptionAction.NonSpecific)) {
2276 // Without a name, we cannot continue parsing the sections and groups within.
2277 // Skip the entire section.
2279 xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
2280 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
2282 else {
2283 string configKey = CombineConfigKey(parentConfigKey, tagName);
2285 FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
2286 if (factoryRecord != null) {
2287 // Error: duplicate sectionGroup declaration
2288 xmlUtil.SchemaErrors.AddError(
2289 new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
2290 ExceptionAction.Local);
2292 } else {
2293 FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
2294 if (parentFactoryRecord != null) {
2295 configKey = parentFactoryRecord.ConfigKey;
2297 // make sure that an ancestor has not defined a section with the same name as the group
2298 if ( parentFactoryRecord != null &&
2299 (!parentFactoryRecord.IsGroup ||
2300 !parentFactoryRecord.IsEquivalentSectionGroupFactory(Host, typeName))) {
2302 xmlUtil.SchemaErrors.AddError(
2303 new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
2304 ExceptionAction.Local);
2306 parentFactoryRecord = null;
2310 if (parentFactoryRecord != null) {
2311 factoryRecord = parentFactoryRecord.CloneSectionGroup(typeName, xmlUtil.Filename, lineNumber);
2313 else {
2314 factoryRecord = new FactoryRecord(configKey, parentConfigKey, tagName, typeName, xmlUtil.Filename, lineNumber);
2317 factoryList[configKey] = factoryRecord;
2320 // Add any errors we may have encountered
2321 factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
2323 // continue recursive scan
2324 ScanFactoriesRecursive(xmlUtil, configKey, factoryList);
2327 continue;
2330 case KEYWORD_SECTION: {
2331 string tagName = null;
2332 string typeName = null;
2333 ConfigurationAllowDefinition allowDefinition = ConfigurationAllowDefinition.Everywhere;
2334 ConfigurationAllowExeDefinition allowExeDefinition = ConfigurationAllowExeDefinition.MachineToApplication;
2335 OverrideModeSetting overrideModeDefault = OverrideModeSetting.SectionDefault;
2336 bool allowLocation = true;
2337 bool restartOnExternalChanges = true;
2338 bool requirePermission = true;
2339 bool gotType = false;
2341 // parse section attributes
2342 int lineNumber = xmlUtil.Reader.LineNumber;
2343 while (xmlUtil.Reader.MoveToNextAttribute()) {
2344 switch (xmlUtil.Reader.Name) {
2345 case KEYWORD_SECTION_NAME:
2346 tagName = xmlUtil.Reader.Value;
2347 VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false, true);
2348 break;
2350 case KEYWORD_SECTION_TYPE:
2351 xmlUtil.VerifyAndGetNonEmptyStringAttribute(ExceptionAction.Local, out typeName);
2352 gotType = true;
2353 break;
2355 case KEYWORD_SECTION_ALLOWLOCATION:
2356 xmlUtil.VerifyAndGetBooleanAttribute(
2357 ExceptionAction.Local, true, out allowLocation);
2358 break;
2360 case KEYWORD_SECTION_ALLOWEXEDEFINITION:
2361 try {
2362 allowExeDefinition = AllowExeDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
2364 catch (ConfigurationException ce) {
2365 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
2368 break;
2370 case KEYWORD_SECTION_ALLOWDEFINITION:
2371 try {
2372 allowDefinition = AllowDefinitionToEnum(xmlUtil.Reader.Value, xmlUtil);
2374 catch (ConfigurationException ce) {
2375 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
2378 break;
2380 case KEYWORD_SECTION_RESTARTONEXTERNALCHANGES:
2381 xmlUtil.VerifyAndGetBooleanAttribute(
2382 ExceptionAction.Local, true, out restartOnExternalChanges);
2384 break;
2386 case KEYWORD_SECTION_REQUIREPERMISSION:
2387 xmlUtil.VerifyAndGetBooleanAttribute(
2388 ExceptionAction.Local, true, out requirePermission);
2390 break;
2392 case KEYWORD_SECTION_OVERRIDEMODEDEFAULT:
2393 try {
2394 overrideModeDefault = OverrideModeSetting.CreateFromXmlReadValue(
2395 OverrideModeSetting.ParseOverrideModeXmlValue(xmlUtil.Reader.Value, xmlUtil));
2397 // Inherit means Allow when comming from the default value
2398 if (overrideModeDefault.OverrideMode == OverrideMode.Inherit) {
2399 overrideModeDefault.ChangeModeInternal(OverrideMode.Allow);
2403 catch (ConfigurationException ce) {
2404 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
2407 break;
2410 default:
2411 xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Local);
2412 break;
2416 xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
2418 if (!xmlUtil.VerifyRequiredAttribute(
2419 tagName, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {
2422 // Without a name, we cannot continue to create a factoryRecord.
2424 xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true);
2426 else {
2427 // Verify that the Type attribute was present.
2428 // Note that 'typeName' will be null if the attribute was present
2429 // but specified as an empty string.
2430 if (!gotType) {
2431 xmlUtil.AddErrorRequiredAttribute(KEYWORD_SECTION_TYPE, ExceptionAction.Local);
2434 // Disallow names starting with "config" unless it is the special configBuilders section.
2435 if (StringUtil.StartsWith(tagName, "config")) {
2436 Type sectionType = Type.GetType(typeName);
2437 if (!StringUtil.Equals(tagName, RESERVED_SECTION_CONFIGURATION_BUILDERS) || sectionType != ConfigurationBuildersSectionType) {
2438 throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_cannot_begin_with_config), xmlUtil);
2442 string configKey = CombineConfigKey(parentConfigKey, tagName);
2444 FactoryRecord factoryRecord = (FactoryRecord) factoryList[configKey];
2445 if (factoryRecord != null) {
2446 // Error: duplicate section declaration
2447 xmlUtil.SchemaErrors.AddError(
2448 new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined_at_this_level, tagName), xmlUtil),
2449 ExceptionAction.Local);
2450 } else {
2451 FactoryRecord parentFactoryRecord = _parent.FindFactoryRecord(configKey, true);
2452 if (parentFactoryRecord != null) {
2453 configKey = parentFactoryRecord.ConfigKey;
2455 // make sure that an ancestor has not defined a section with the same name as the group
2456 if (parentFactoryRecord.IsGroup) {
2457 xmlUtil.SchemaErrors.AddError(
2458 new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
2459 ExceptionAction.Local);
2461 parentFactoryRecord = null;
2463 else if (!parentFactoryRecord.IsEquivalentSectionFactory(Host, typeName, allowLocation, allowDefinition, allowExeDefinition, restartOnExternalChanges, requirePermission)) {
2464 xmlUtil.SchemaErrors.AddError(
2465 new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_already_defined, tagName), xmlUtil),
2466 ExceptionAction.Local);
2468 parentFactoryRecord = null;
2472 if (parentFactoryRecord != null) {
2473 // Note - Clone will propagate the IsFromTrustedConfigRecord bit,
2474 // which is what we want - if this record is a duplicate of an ancestor,
2475 // the ancestor may be from a trusted config record.
2476 factoryRecord = parentFactoryRecord.CloneSection(xmlUtil.Filename, lineNumber);
2478 else {
2479 factoryRecord = new FactoryRecord(
2480 configKey,
2481 parentConfigKey,
2482 tagName,
2483 typeName,
2484 allowLocation,
2485 allowDefinition,
2486 allowExeDefinition,
2487 overrideModeDefault,
2488 restartOnExternalChanges,
2489 requirePermission,
2490 _flags[IsTrusted],
2491 false, // isUndeclared
2492 xmlUtil.Filename,
2493 lineNumber);
2496 factoryList[configKey] = factoryRecord;
2499 // Add any errors we may have encountered
2500 factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
2503 break;
2505 case KEYWORD_REMOVE: {
2506 string name = null;
2507 int lineNumber = -1;
2509 // parse attributes
2510 while (xmlUtil.Reader.MoveToNextAttribute()) {
2511 if (xmlUtil.Reader.Name != KEYWORD_SECTION_NAME) {
2512 xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.NonSpecific);
2515 name = xmlUtil.Reader.Value;
2516 lineNumber = xmlUtil.Reader.LineNumber;
2518 xmlUtil.Reader.MoveToElement();
2520 if (xmlUtil.VerifyRequiredAttribute(
2521 name, KEYWORD_SECTION_NAME, ExceptionAction.NonSpecific)) {
2523 VerifySectionName(name, xmlUtil, ExceptionAction.NonSpecific, false, true);
2526 break;
2528 case KEYWORD_CLEAR: {
2529 xmlUtil.VerifyNoUnrecognizedAttributes(ExceptionAction.NonSpecific);
2531 break;
2533 default:
2534 xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
2535 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
2536 positionedAtNextElement = true;
2537 break;
2540 if (!positionedAtNextElement) {
2541 // Need to read to next element, and check if an unrecognized child
2542 // element is found.
2543 xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
2545 // unrecognized children are not allowed in <configSections>
2546 if (xmlUtil.Reader.Depth > depth + 1) {
2547 xmlUtil.AddErrorUnrecognizedElement(ExceptionAction.NonSpecific);
2549 // Lets try to backup to where we are suppose to be
2550 while (xmlUtil.Reader.Depth > (depth + 1)) {
2551 xmlUtil.ReadToNextElement();
2558 // ExeDefinitionToEnum
2560 // Translate an ExeDefinition string from the Declaration in a file
2561 // to the appropriate enumeration
2563 // Parameters:
2564 // allowExeDefinition - string representation of value
2565 // xmlUtil [optional] - can provide better error
2567 static internal ConfigurationAllowExeDefinition
2568 AllowExeDefinitionToEnum(string allowExeDefinition, XmlUtil xmlUtil)
2570 switch (allowExeDefinition)
2572 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY:
2573 return ConfigurationAllowExeDefinition.MachineOnly;
2575 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION:
2576 return ConfigurationAllowExeDefinition.MachineToApplication;
2578 case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOROAMING:
2579 return ConfigurationAllowExeDefinition.MachineToRoamingUser;
2581 case KEYWORD_SECTION_ALLOWEXEDEFINITION_MACHTOLOCAL:
2582 return ConfigurationAllowExeDefinition.MachineToLocalUser;
2584 default:
2585 throw new ConfigurationErrorsException(
2586 SR.GetString(SR.Config_section_allow_exe_definition_attribute_invalid),
2587 xmlUtil);
2591 static internal ConfigurationAllowDefinition
2592 AllowDefinitionToEnum(string allowDefinition, XmlUtil xmlUtil) {
2593 switch (xmlUtil.Reader.Value) {
2594 case KEYWORD_SECTION_ALLOWDEFINITION_EVERYWHERE:
2595 return ConfigurationAllowDefinition.Everywhere;
2597 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINEONLY:
2598 return ConfigurationAllowDefinition.MachineOnly;
2600 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOAPPLICATION:
2601 return ConfigurationAllowDefinition.MachineToApplication;
2603 case KEYWORD_SECTION_ALLOWDEFINITION_MACHINETOWEBROOT:
2604 return ConfigurationAllowDefinition.MachineToWebRoot;
2606 default:
2607 throw new ConfigurationErrorsException(
2608 SR.GetString(SR.Config_section_allow_definition_attribute_invalid),
2609 xmlUtil);
2613 static internal string CombineConfigKey(string parentConfigKey, string tagName) {
2614 if (String.IsNullOrEmpty(parentConfigKey)) {
2615 return tagName;
2618 if (String.IsNullOrEmpty(tagName)) {
2619 return parentConfigKey;
2622 return parentConfigKey + "/" + tagName;
2625 static internal void SplitConfigKey(string configKey, out string group, out string name) {
2626 int lastSlash = configKey.LastIndexOf('/');
2627 if (lastSlash == -1) {
2628 group = string.Empty;
2629 name = configKey;
2631 else {
2632 group = configKey.Substring(0, lastSlash);
2633 name = configKey.Substring(lastSlash + 1);
2637 [System.Diagnostics.Conditional("DBG")]
2638 private void DebugValidateIndirectInputs(SectionRecord sectionRecord) {
2639 if (_parent.IsRootConfig) {
2640 return;
2643 // Verify that for each indirect input, its target config path is a child path of _parent.
2644 // That's the definition of indirect input.
2645 for(int i = sectionRecord.IndirectLocationInputs.Count-1; i >= 0; i--) {
2646 SectionInput input = sectionRecord.IndirectLocationInputs[i];
2648 // Get the override mode starting from the closest input.
2649 Debug.Assert(UrlPath.IsSubpath(_parent.ConfigPath, input.SectionXmlInfo.TargetConfigPath));
2653 // Return the lock mode for a section as comming from parent config levels
2654 private OverrideMode ResolveOverrideModeFromParent(string configKey, out OverrideMode childLockMode) {
2656 // When the current record is a location config level we are a direct child of the config level of the actual
2657 // config file inside which the location tag is. For example we have a file d:\inetpub\wwwroot\web.config which
2658 // containts <location path="Sub"> then "this" will be the config level inside the location tag and this.Parent
2659 // is the config level of d:\inetpub\wwwroot\web.config.
2661 // What we will do to come up with the result is:
2662 // 1) Try to find an existing section record somewhere above us.
2663 // If we find an existing section record then it will have the effective value of the lock mode
2664 // that applies to us in it's LockChidlren. We dont need to go further up once we find a section record
2665 // as it has the lock mode of all it's parents accumulated
2667 // There is one huge trick though - Location config records are different ( see begining of the func for what a location config record is )
2668 // A location config record is not locked if the config level of the web.config file in which it lives is not locked.
2669 // I.e. when we are looking for the effective value for a location config we have two cases
2670 // a) There is a section record in our immediate parent ( remember our immediate parent is the config file in which we /as a location tag/ are defined )
2671 // In this case our lock mode is not the LockChildren of this section record because this lock mode applies to child config levels in child config files
2672 // The real lock mode for us is the Locked mode of the section record in self.
2673 // b) There is no section record in our immediate parent - in this case the locking is the same as for normal config - LockChildren value of any section
2674 // record we may find above us.
2676 // 2) If we can't find an existing section record we have two cases again:
2677 // a) We are at the section declaration level - at this level a section is always unlocked by definition
2678 // If this wasnt so there would be no way to unlock a section that is locked by default
2679 // A Location config is a bit wierd again in a sence that a location config is unlocked if its in the config file where the section is declared
2680 // I.e. if "this" is a location record then a section is unconditionally unlocked if "this.Parent" is the section declaration level
2681 // b) We are not at section declaration level - in this case the result is whatever the default lock mode for the section is ( remember
2682 // that we fall back to the default since we couldnt find a section record with explicit lock mode nowhere above us)
2684 // I sure hope that made some sense!
2687 OverrideMode mode = OverrideMode.Inherit;
2688 BaseConfigurationRecord parent = Parent;
2689 BaseConfigurationRecord immediateParent = Parent;
2691 childLockMode = OverrideMode.Inherit;
2693 // Walk the hierarchy until we find an explicit setting for lock state at a config level or we reach to root
2694 while (!parent.IsRootConfig && (mode == OverrideMode.Inherit)) {
2695 SectionRecord sectionRecord = parent.GetSectionRecord(configKey, true);
2697 if (sectionRecord != null) {
2699 // Check for 1a
2700 if (IsLocationConfig && object.ReferenceEquals(immediateParent, parent)) {
2701 // Apply case 1a
2702 mode = sectionRecord.Locked ? OverrideMode.Deny : OverrideMode.Allow;
2704 // In this specific case the lock mode for our children is whatever the children of our parent should inherit
2705 // For example imagine a web.config which has a <location path="." overrideMode="Deny"> and we open "locationSubPath" from this web.config
2706 // The lock for the section is not Deny and will be allow ( see the code line above ). However the chidlren of this location tag
2707 // inherit the lock that applies to the children of the web.config file itself
2708 childLockMode = sectionRecord.LockChildren ? OverrideMode.Deny : OverrideMode.Allow;
2710 else {
2711 mode = sectionRecord.LockChildren ? OverrideMode.Deny : OverrideMode.Allow;
2713 // When the lock mode is comming from a parent level the
2714 // lock mode that applies to children of "this" is the same as what applies to "this"
2715 childLockMode = mode;
2719 parent = parent._parent;
2722 // Case 2
2723 if (mode == OverrideMode.Inherit) {
2725 Debug.Assert(FindFactoryRecord(configKey, true) != null);
2727 bool atDeclarationLevel = false;
2728 OverrideMode defaultMode = FindFactoryRecord(configKey, true).OverrideModeDefault.OverrideMode;
2730 if (IsLocationConfig) {
2731 atDeclarationLevel = this.Parent.GetFactoryRecord(configKey, true) != null;
2733 else {
2734 atDeclarationLevel = this.GetFactoryRecord(configKey, true) != null;
2737 if (!atDeclarationLevel) {
2739 // 2b
2740 /////////
2742 // Lock mode for children and self is the same since the default value is comming
2743 // from a parent level and hence - applies to both
2744 childLockMode = mode = defaultMode;
2746 Debug.Assert(mode != OverrideMode.Inherit); // Remember that the default is never Inherit
2748 else {
2750 // 2a
2751 ////////
2753 // Self is always allow at section declaration level
2754 // Child lock mode is the default value ( remember we are here because no explici mode was set anywhere above us )
2756 mode = OverrideMode.Allow;
2757 childLockMode = defaultMode;
2761 // This function must return Allow or Deny
2762 Debug.Assert(mode != OverrideMode.Inherit);
2764 return mode;
2767 protected OverrideMode GetSectionLockedMode(string configKey) {
2768 OverrideMode dummy = OverrideMode.Inherit;
2770 return GetSectionLockedMode(configKey, out dummy);
2773 // Return the current lock mode for a section
2774 protected OverrideMode GetSectionLockedMode(string configKey, out OverrideMode childLockMode) {
2776 OverrideMode result = OverrideMode.Inherit;
2778 SectionRecord sectionRecord = GetSectionRecord(configKey, true);
2780 // If there is a section record it has the effective locking settings resolved
2781 // There is no need to do ResolveOverrideModeFromParent because it was done in:
2782 // 1) In EnsureSectionRecord when the section record was creteted
2783 // 2) Right after the SectionRecord was created without initialization of the lock settings
2784 // in this:ScanSectionsRecursive().
2785 // As long as nobody uses EnsureSectionRecordUnsafe this method will be returning the correct
2786 // lock value only by looking at the section record
2788 if (sectionRecord != null) {
2789 result = sectionRecord.Locked ? OverrideMode.Deny : OverrideMode.Allow;
2790 childLockMode = sectionRecord.LockChildren ? OverrideMode.Deny : OverrideMode.Allow;
2792 else {
2793 result = ResolveOverrideModeFromParent(configKey, out childLockMode);
2796 return result;
2799 private void ScanSections(XmlUtil xmlUtil) {
2800 ScanSectionsRecursive(xmlUtil, string.Empty, false, null, OverrideModeSetting.LocationDefault, false);
2803 private void ScanSectionsRecursive(
2804 XmlUtil xmlUtil,
2805 string parentConfigKey,
2806 bool inLocation,
2807 string locationSubPath,
2808 OverrideModeSetting overrideMode,
2809 bool skipInChildApps) {
2811 // discard any accumulated local errors
2812 xmlUtil.SchemaErrors.ResetLocalErrors();
2814 int depth;
2816 // only move to child nodes when not on first level (we've already passed the first <configsections>)
2817 if (parentConfigKey.Length == 0 && !inLocation) {
2818 depth = 0;
2820 else {
2821 depth = xmlUtil.Reader.Depth;
2822 xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
2825 while (xmlUtil.Reader.Depth == depth + 1) {
2827 string tagName = xmlUtil.Reader.Name;
2830 // Check for reserved elements before looking up the factory,
2831 // which may have the same name if it is in error.
2833 if (tagName == KEYWORD_CONFIGSECTIONS) {
2834 // Error: duplicate <configSections> tag, or <configSections> not the first tag under <configuration>
2835 xmlUtil.SchemaErrors.AddError(
2836 new ConfigurationErrorsException(SR.GetString(SR.Config_client_config_too_many_configsections_elements, tagName), xmlUtil),
2837 ExceptionAction.NonSpecific);
2839 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
2840 continue;
2843 if (tagName == KEYWORD_LOCATION) {
2844 if (parentConfigKey.Length > 0 || inLocation) {
2845 // Error: <location> section not at top level
2846 xmlUtil.SchemaErrors.AddError(
2847 new ConfigurationErrorsException(SR.GetString(SR.Config_location_location_not_allowed), xmlUtil),
2848 ExceptionAction.Global);
2850 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
2852 else {
2853 // Recurse into the location section
2854 ScanLocationSection(xmlUtil);
2857 continue;
2860 string configKey = CombineConfigKey(parentConfigKey, tagName);
2861 FactoryRecord factoryRecord = FindFactoryRecord(configKey, true);
2863 if (factoryRecord == null) {
2865 // Unregistered configuration section
2867 // At runtime, it is a local error to have an unrecognized section.
2868 // By treating it as local we avoid throwing an error if the
2869 // section is encountered within a location section, just as we treat
2870 // other section errors in a location tag.
2872 // At designtime, we do not consider it an error, so that programs
2873 // that worked on version N config files can continue to work with
2874 // version N+1 config files that may introduce new sections.
2876 if (!ClassFlags[ClassIgnoreLocalErrors]) {
2877 xmlUtil.SchemaErrors.AddError(
2878 new ConfigurationErrorsException(SR.GetString(SR.Config_unrecognized_configuration_section, configKey), xmlUtil),
2879 ExceptionAction.Local);
2882 VerifySectionName(tagName, xmlUtil, ExceptionAction.Local, false);
2884 factoryRecord = new FactoryRecord(
2885 configKey,
2886 parentConfigKey,
2887 tagName,
2888 typeof(DefaultSection).AssemblyQualifiedName,
2889 true, // allowLocation
2890 ConfigurationAllowDefinition.Everywhere,
2891 ConfigurationAllowExeDefinition.MachineToRoamingUser,
2892 OverrideModeSetting.SectionDefault,
2893 true, // restartOnExternalChanges
2894 true, // requirePermission
2895 _flags[IsTrusted],
2896 true, // isUndeclared
2897 null,
2898 -1);
2900 // Add any errors we may have encountered to the factory record,
2901 // so that child config that also refer to this unrecognized section
2902 // get the error.
2903 factoryRecord.AddErrors(xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(true));
2905 // Add the factory to the list of factories
2906 EnsureFactories()[configKey] = factoryRecord;
2909 if (factoryRecord.IsGroup) {
2911 // Section Group
2913 if (factoryRecord.HasErrors) {
2914 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
2916 else {
2917 if (xmlUtil.Reader.AttributeCount > 0) {
2918 // We allow unrecognized attributes for backward compatibility (VSWhidbey 516534)
2919 // However, we will still throw if the unrecognized attribute is reserved.
2920 while (xmlUtil.Reader.MoveToNextAttribute()) {
2921 if (IsReservedAttributeName(xmlUtil.Reader.Name)) {
2922 xmlUtil.AddErrorReservedAttribute(ExceptionAction.NonSpecific);
2926 xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
2929 // Recurse into group definition
2930 ScanSectionsRecursive(xmlUtil, configKey, inLocation, locationSubPath, overrideMode, skipInChildApps);
2933 else {
2935 // Section
2937 configKey = factoryRecord.ConfigKey;
2938 string fileName = xmlUtil.Filename;
2939 int lineNumber = xmlUtil.LineNumber;
2940 string rawXml = null;
2941 string configSource = null;
2942 string configSourceStreamName = null;
2943 object configSourceStreamVersion = null;
2944 string configBuilderName = null;
2945 string protectionProviderName = null;
2946 OverrideMode sectionLockMode = OverrideMode.Inherit;
2947 OverrideMode sectionChildLockMode = OverrideMode.Inherit;
2948 bool positionedAtNextElement = false;
2949 bool isFileInput = (locationSubPath == null);
2951 if (!factoryRecord.HasErrors) {
2952 // We have a valid factoryRecord for a section
2953 if (inLocation && factoryRecord.AllowLocation == false) {
2954 xmlUtil.SchemaErrors.AddError(
2955 new ConfigurationErrorsException(SR.GetString(SR.Config_section_cannot_be_used_in_location), xmlUtil),
2956 ExceptionAction.Local);
2959 // Verify correctness for file inputs.
2960 if (isFileInput) {
2961 // Verify that the section is unique
2962 SectionRecord sectionRecord = GetSectionRecord(configKey, true);
2963 if (sectionRecord != null && sectionRecord.HasFileInput) {
2964 if (!(factoryRecord.IsIgnorable())) {
2965 xmlUtil.SchemaErrors.AddError(
2966 new ConfigurationErrorsException(SR.GetString(SR.Config_sections_must_be_unique), xmlUtil),
2967 ExceptionAction.Local);
2972 // Verify that the definition is allowed.
2973 try {
2974 VerifyDefinitionAllowed(factoryRecord, _configPath, xmlUtil);
2976 catch (ConfigurationException ce) {
2977 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
2981 // Verify that section is unlocked, both for file and location inputs.
2982 sectionLockMode = GetSectionLockedMode(configKey, out sectionChildLockMode);
2984 if (sectionLockMode == OverrideMode.Deny) {
2985 xmlUtil.SchemaErrors.AddError( new ConfigurationErrorsException(SR.GetString(SR.Config_section_locked), xmlUtil),
2986 ExceptionAction.Local);
2989 // check for configSource or protectionProvider
2990 if (xmlUtil.Reader.AttributeCount >= 1) {
2991 // First do all the attributes reading without advancing the reader.
2993 string configSourceAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_CONFIGSOURCE);
2994 if (configSourceAttribute != null) {
2995 try {
2996 configSource = NormalizeConfigSource(configSourceAttribute, xmlUtil);
2998 catch (ConfigurationException ce) {
2999 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
3002 if (xmlUtil.Reader.AttributeCount != 1) {
3003 // Error: elements with configSource should not have other attributes
3004 xmlUtil.SchemaErrors.AddError(
3005 new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil),
3006 ExceptionAction.Local);
3010 string protectionProviderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_PROTECTION_PROVIDER);
3011 if (protectionProviderAttribute != null) {
3012 try {
3013 protectionProviderName = ValidateProtectionProviderAttribute(protectionProviderAttribute, xmlUtil);
3015 catch (ConfigurationException ce) {
3016 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
3019 if (xmlUtil.Reader.AttributeCount != 1) {
3020 // Error: elements with protectionProvider should not have other attributes
3021 xmlUtil.SchemaErrors.AddError(
3022 new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_syntax_error), xmlUtil),
3023 ExceptionAction.Local);
3027 string configBuilderAttribute = xmlUtil.Reader.GetAttribute(KEYWORD_CONFIG_BUILDER);
3028 if (configBuilderAttribute != null) {
3029 try {
3030 configBuilderName = ValidateConfigBuilderAttribute(configBuilderAttribute, xmlUtil);
3032 catch (ConfigurationException ce) {
3033 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Local);
3037 // The 2nd part of the configSource check requires advancing the reader.
3038 // Please note that this part should be done only AFTER all other attributes
3039 // checking are done.
3040 if (configSourceAttribute != null) {
3041 if (!xmlUtil.Reader.IsEmptyElement) {
3042 while (xmlUtil.Reader.Read()) {
3043 XmlNodeType t = xmlUtil.Reader.NodeType;
3044 if (t == XmlNodeType.EndElement)
3045 break;
3047 if (t != XmlNodeType.Comment) {
3048 // Error: elements with configSource should not subelements other than comments
3049 xmlUtil.SchemaErrors.AddError(
3050 new ConfigurationErrorsException(SR.GetString(SR.Config_source_syntax_error), xmlUtil),
3051 ExceptionAction.Local);
3053 if (t == XmlNodeType.Element) {
3054 xmlUtil.StrictSkipToOurParentsEndElement(ExceptionAction.NonSpecific);
3056 else {
3057 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
3060 positionedAtNextElement = true;
3061 break;
3068 if (configSource != null) {
3069 try {
3070 try {
3071 configSourceStreamName = Host.GetStreamNameForConfigSource(ConfigStreamInfo.StreamName, configSource);
3073 catch (Exception e) {
3074 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_source_invalid), e, xmlUtil);
3076 ValidateUniqueConfigSource(configKey, configSourceStreamName, configSource, xmlUtil);
3077 configSourceStreamVersion = MonitorStream(configKey, configSource, configSourceStreamName);
3079 catch (ConfigurationException ex) {
3080 xmlUtil.SchemaErrors.AddError(ex, ExceptionAction.Local);
3085 // prefetch the raw xml
3087 if (!xmlUtil.SchemaErrors.HasLocalErrors) {
3088 if (configSource == null && ShouldPrefetchRawXml(factoryRecord)) {
3089 Debug.Assert(!positionedAtNextElement, "!positionedAtNextElement");
3091 rawXml = xmlUtil.CopySection();
3092 if (xmlUtil.Reader.NodeType != XmlNodeType.Element) {
3093 xmlUtil.VerifyIgnorableNodeType(ExceptionAction.NonSpecific);
3094 xmlUtil.StrictReadToNextElement(ExceptionAction.NonSpecific);
3097 positionedAtNextElement = true;
3102 // Get the list of errors before advancing the reader
3103 List<ConfigurationException> localErrors = xmlUtil.SchemaErrors.RetrieveAndResetLocalErrors(isFileInput);
3105 // advance the reader to the next element
3106 if (!positionedAtNextElement) {
3107 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
3110 // Add the input either to:
3111 // 1. The file input at the current config level, or
3112 // 2. LocationSections, where it will be used in sub paths
3113 bool addInput = true;
3115 if (isFileInput) {
3116 // If isFileInput==true, Input added will be used against this config level.
3117 // Need to check if we need to skip it due to inheritInChildApplications.
3119 if (ShouldSkipDueToInheritInChildApplications(skipInChildApps)) {
3120 addInput = false;
3123 else {
3124 if (!_flags[SupportsLocation]) {
3125 // Skip if we have a location input but we don't support location tag.
3126 addInput = false;
3130 if (addInput) {
3132 string targetConfigPath = (locationSubPath == null) ? _configPath : null;
3134 SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
3135 configKey, _configPath, targetConfigPath, locationSubPath,
3136 fileName, lineNumber, ConfigStreamInfo.StreamVersion, rawXml,
3137 configSource, configSourceStreamName, configSourceStreamVersion,
3138 configBuilderName, protectionProviderName, overrideMode, skipInChildApps);
3140 if (locationSubPath == null) {
3142 // Add this file input to the section record
3145 // We've already checked for locked above, so use skip the second check
3146 // and set the locked bit.
3147 SectionRecord sectionRecord = EnsureSectionRecordUnsafe(configKey, true);
3149 // Since we called EnsureSectionRecordUnsafe the section record does not have its lock mode resolved
3150 // but we have it in sectionLockMode and childLockMode. Apply it now
3151 sectionRecord.ChangeLockSettings(sectionLockMode, sectionChildLockMode);
3153 // Note that we first apply the lock mode comming from parent levels ( the line above ) and then
3154 // add the file input since the file input takes precedence over whats comming from parent
3155 SectionInput fileInput = new SectionInput(sectionXmlInfo, localErrors);
3156 sectionRecord.AddFileInput(fileInput);
3160 else {
3162 // Add this location input to this list of location sections
3164 LocationSectionRecord locationSectionRecord = new LocationSectionRecord(sectionXmlInfo, localErrors);
3165 EnsureLocationSections().Add(locationSectionRecord);
3172 private void ScanLocationSection(XmlUtil xmlUtil) {
3173 string locationSubPath = null;
3174 bool inheritInChildApp = true;
3175 int errorCountBeforeScan = xmlUtil.SchemaErrors.GlobalErrorCount;
3176 OverrideModeSetting overrideMode = OverrideModeSetting.LocationDefault;
3177 bool overrideModeInit = false;
3179 // Get the location section attributes
3180 while (xmlUtil.Reader.MoveToNextAttribute()) {
3181 switch (xmlUtil.Reader.Name) {
3182 case KEYWORD_LOCATION_PATH:
3183 locationSubPath = xmlUtil.Reader.Value;
3184 break;
3186 case KEYWORD_LOCATION_ALLOWOVERRIDE:
3187 // Check that allowOverride and OverrideMode werent specified at the same time
3188 if (overrideModeInit == true){
3189 xmlUtil.SchemaErrors.AddError(new ConfigurationErrorsException(SR.GetString(SR.Invalid_override_mode_declaration), xmlUtil), ExceptionAction.Global);
3191 else {
3192 bool value = true;
3194 // Read the value
3195 xmlUtil.VerifyAndGetBooleanAttribute(
3196 ExceptionAction.Global, true, out value);
3198 overrideMode = OverrideModeSetting.CreateFromXmlReadValue(value);
3199 overrideModeInit = true;
3202 break;
3204 case KEYWORD_LOCATION_OVERRIDEMODE:
3205 if (overrideModeInit == true){
3206 xmlUtil.SchemaErrors.AddError(new ConfigurationErrorsException(SR.GetString(SR.Invalid_override_mode_declaration), xmlUtil), ExceptionAction.Global);
3208 else {
3209 overrideMode = OverrideModeSetting.CreateFromXmlReadValue(
3210 OverrideModeSetting.ParseOverrideModeXmlValue(xmlUtil.Reader.Value, xmlUtil));
3211 overrideModeInit = true;
3213 break;
3216 case KEYWORD_LOCATION_INHERITINCHILDAPPLICATIONS:
3217 xmlUtil.VerifyAndGetBooleanAttribute(
3218 ExceptionAction.Global, true, out inheritInChildApp);
3220 break;
3222 default:
3223 xmlUtil.AddErrorUnrecognizedAttribute(ExceptionAction.Global);
3224 break;
3228 xmlUtil.Reader.MoveToElement(); // if on an attribute move back to the element
3230 try {
3231 locationSubPath = NormalizeLocationSubPath(locationSubPath, xmlUtil);
3233 // VSWhidbey 535595
3234 // See attached email in the bug. Basically, we decided to throw if we see one of these
3235 // in machine.config or root web.config:
3236 // <location path="." inheritInChildApplications="false" >
3237 // <location inheritInChildApplications="false" >
3239 // To detect whetherewe're machine.config or root web.config, the current fix is to use
3240 // Host.IsDefinitionAllowed. Instead of this we should invent a new method in
3241 // IInternalConfigHost to return whether a configPath can be part of an app or not.
3242 // But since it's Whidbey RC "Ask Mode" I chose not to do it due to bigger code churn.
3245 if (locationSubPath == null &&
3246 !inheritInChildApp &&
3247 Host.IsDefinitionAllowed(_configPath, ConfigurationAllowDefinition.MachineToWebRoot, ConfigurationAllowExeDefinition.MachineOnly)) {
3248 throw new ConfigurationErrorsException(SR.GetString(SR.Location_invalid_inheritInChildApplications_in_machine_or_root_web_config), xmlUtil);
3251 catch (ConfigurationErrorsException ce) {
3252 xmlUtil.SchemaErrors.AddError(ce, ExceptionAction.Global);
3255 // Skip over this location section if there are errors
3256 if (xmlUtil.SchemaErrors.GlobalErrorCount > errorCountBeforeScan) {
3257 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
3258 return;
3261 // Scan elements of the location section if the path is the current path.
3262 // We do not add <location path="." /> to the _locationSections list.
3263 if (locationSubPath == null) {
3264 ScanSectionsRecursive(xmlUtil, string.Empty, true, null, overrideMode, !inheritInChildApp);
3265 return;
3268 // Skip over location sections for client config
3269 if (!_flags[SupportsLocation]) {
3270 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
3271 return;
3274 // WOS 1955773: (Perf) 4,000 location sections in web.config file degrades working set
3275 // Skip over location sections that don't apply to this (application) host
3276 // WOS 1983387: do this for the runtime record only. It's a valid scenario for
3277 // mgt config record
3278 IInternalConfigHost host = Host;
3279 if ((this is RuntimeConfigurationRecord) && host != null && locationSubPath.Length != 0 && locationSubPath[0] != '.') {
3281 // The application's config path is global to the AppDomain
3282 if (s_appConfigPath == null) {
3283 object ctx = ConfigContext;
3284 if (ctx != null) {
3285 string appConfigPath = ctx.ToString();
3286 Interlocked.CompareExchange(ref s_appConfigPath, appConfigPath, null);
3290 // If targetConfigPath is not upstream or downstream of the application's config path,
3291 // skip this location section.
3293 // Example #1: <location path="Site1"> has a targetConfigPath of "machine/webroot/1". This applies
3294 // to Site1, whose application config path is "machine/webroot/1", but it does not apply
3295 // to Site2, whose application config path is "machine/webroot/2"
3297 // Example #2: <location path="subdir"> has a targetConfigPath of "machine/webroot/1/root/subdir".
3298 // This applies to an application with an application config path of "machine/webroot/1/root/subdir/app".
3300 string targetConfigPath = host.GetConfigPathFromLocationSubPath(_configPath, locationSubPath);
3301 if (!StringUtil.StartsWithIgnoreCase(s_appConfigPath, targetConfigPath)
3302 && !StringUtil.StartsWithIgnoreCase(targetConfigPath, s_appConfigPath)) {
3303 xmlUtil.StrictSkipToNextElement(ExceptionAction.NonSpecific);
3304 return;
3308 AddLocation(locationSubPath);
3309 ScanSectionsRecursive(xmlUtil, string.Empty, true, locationSubPath, overrideMode, !inheritInChildApp);
3313 // AddLocation
3315 // If you wish to keep track of the Location Fields, then use this
3317 protected virtual void AddLocation(string LocationSubPath) {}
3320 // Resolve information about a location section at the time that the location section
3321 // is being used by child configuration records. This allows us to:
3322 // * Delay determining the configuration path for the location record until the sites section is available.
3323 // * Delay reporting bad location paths until the location record has to be used.
3325 private void ResolveLocationSections() {
3326 if (!_flags[IsLocationListResolved]) {
3327 // Resolve outside of any lock
3328 if (!_parent.IsRootConfig) {
3329 _parent.ResolveLocationSections();
3332 lock (this) {
3333 if (!_flags[IsLocationListResolved]) {
3334 if (_locationSections != null) {
3336 // Create dictionary that maps configPaths to (dictionary that maps sectionNames to locationSectionRecords)
3338 HybridDictionary locationConfigPaths = new HybridDictionary(true);
3339 foreach (LocationSectionRecord locationSectionRecord in _locationSections) {
3341 // Resolve the target config path
3343 string targetConfigPath = Host.GetConfigPathFromLocationSubPath(_configPath, locationSectionRecord.SectionXmlInfo.SubPath);
3344 locationSectionRecord.SectionXmlInfo.TargetConfigPath = targetConfigPath;
3347 // Check uniqueness
3349 HybridDictionary locationSectionRecordDictionary = (HybridDictionary) locationConfigPaths[targetConfigPath];
3350 if (locationSectionRecordDictionary == null) {
3351 locationSectionRecordDictionary = new HybridDictionary(false);
3352 locationConfigPaths.Add(targetConfigPath, locationSectionRecordDictionary);
3355 LocationSectionRecord duplicateRecord = (LocationSectionRecord) locationSectionRecordDictionary[locationSectionRecord.ConfigKey];
3356 FactoryRecord factoryRecord = null;
3357 if (duplicateRecord == null) {
3358 locationSectionRecordDictionary.Add(locationSectionRecord.ConfigKey, locationSectionRecord);
3360 else {
3361 factoryRecord = FindFactoryRecord(locationSectionRecord.ConfigKey, true);
3362 if (factoryRecord == null || !(factoryRecord.IsIgnorable())) {
3363 if (!duplicateRecord.HasErrors) {
3364 duplicateRecord.AddError(
3365 new ConfigurationErrorsException(
3366 SR.GetString(SR.Config_sections_must_be_unique),
3367 duplicateRecord.SectionXmlInfo));
3370 locationSectionRecord.AddError(
3371 new ConfigurationErrorsException(
3372 SR.GetString(SR.Config_sections_must_be_unique),
3373 locationSectionRecord.SectionXmlInfo));
3378 // Check if the definition is allowed
3380 if (factoryRecord == null)
3381 factoryRecord = FindFactoryRecord(locationSectionRecord.ConfigKey, true);
3382 if (!factoryRecord.HasErrors) {
3383 try {
3384 VerifyDefinitionAllowed(factoryRecord, targetConfigPath, locationSectionRecord.SectionXmlInfo);
3386 catch (ConfigurationException e) {
3387 locationSectionRecord.AddError(e);
3394 // Check location section for being locked.
3396 BaseConfigurationRecord parent = _parent;
3397 while (!parent.IsRootConfig) {
3398 foreach (LocationSectionRecord locationSectionRecord in this._locationSections) {
3400 bool locked = false;
3403 // It is an error if a parent section with the same configKey is locked.
3405 SectionRecord sectionRecord = parent.GetSectionRecord(locationSectionRecord.ConfigKey, true);
3406 if ( sectionRecord != null &&
3407 (sectionRecord.LockChildren || sectionRecord.Locked)) {
3409 locked = true;
3411 else {
3413 // It is an error if a parent configuration file locks a section for the
3414 // locationConfigPath or any sub-path of the locationConfigPath.
3416 if (parent._locationSections != null) {
3417 string targetConfigPath = locationSectionRecord.SectionXmlInfo.TargetConfigPath;
3419 foreach (LocationSectionRecord parentLocationSectionRecord in parent._locationSections) {
3420 string parentTargetConfigPath = parentLocationSectionRecord.SectionXmlInfo.TargetConfigPath;
3422 if ( parentLocationSectionRecord.SectionXmlInfo.OverrideModeSetting.IsLocked &&
3423 locationSectionRecord.ConfigKey == parentLocationSectionRecord.ConfigKey &&
3424 UrlPath.IsEqualOrSubpath(targetConfigPath, parentTargetConfigPath)) {
3426 locked = true;
3427 break;
3433 if (locked) {
3434 locationSectionRecord.AddError(new ConfigurationErrorsException(
3435 SR.GetString(SR.Config_section_locked),
3436 locationSectionRecord.SectionXmlInfo));
3440 parent = parent._parent;
3446 _flags[IsLocationListResolved] = true;
3453 // VerifyDefinitionAllowed
3455 // Verify that the Definition is allowed at this
3456 // place.
3458 // For example, if this config record is an application then
3459 // make sure the section say's it can be defined in an
3460 // application
3462 private void VerifyDefinitionAllowed(FactoryRecord factoryRecord, string configPath, IConfigErrorInfo errorInfo) {
3463 Host.VerifyDefinitionAllowed(configPath, factoryRecord.AllowDefinition, factoryRecord.AllowExeDefinition, errorInfo);
3466 internal bool IsDefinitionAllowed(ConfigurationAllowDefinition allowDefinition, ConfigurationAllowExeDefinition allowExeDefinition) {
3467 return Host.IsDefinitionAllowed(_configPath, allowDefinition, allowExeDefinition);
3470 static protected void VerifySectionName(string name, XmlUtil xmlUtil, ExceptionAction action, bool allowImplicit, bool allowConfigNames = false) {
3471 try {
3472 VerifySectionName(name, (IConfigErrorInfo) xmlUtil, allowImplicit, allowConfigNames);
3474 catch (ConfigurationErrorsException ce) {
3475 xmlUtil.SchemaErrors.AddError(ce, action);
3479 // Check if the section name contains reserved words from the config system,
3480 // and is a valid name for an XML Element.
3481 static protected void VerifySectionName(string name, IConfigErrorInfo errorInfo, bool allowImplicit, bool allowConfigNames = false) {
3482 if (String.IsNullOrEmpty(name)) {
3483 throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_invalid), errorInfo);
3486 // must be a valid name in xml, so that it can be used as an element
3487 // n.b. - it also excludes forward slash '/'
3488 try {
3489 XmlConvert.VerifyName(name);
3491 // Do not let the exception propagate as an XML exception,
3492 // for we want errors in the section name to be treated as local errors,
3493 // not global ones.
3494 catch (Exception e) {
3495 throw ExceptionUtil.WrapAsConfigException(SR.GetString(SR.Config_tag_name_invalid), e, errorInfo);
3498 if (IsImplicitSection(name)) {
3499 if (allowImplicit) {
3500 // avoid test below for strings starting with "config"
3501 return;
3503 else {
3504 throw new ConfigurationErrorsException(SR.GetString(SR.Cannot_declare_or_remove_implicit_section, name), errorInfo);
3508 if (!allowConfigNames && StringUtil.StartsWith(name, "config")) {
3509 throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_cannot_begin_with_config), errorInfo);
3512 if (name == KEYWORD_LOCATION) {
3513 throw new ConfigurationErrorsException(SR.GetString(SR.Config_tag_name_cannot_be_location), errorInfo);
3518 From http://www.w3.org/Addressing/
3520 reserved = ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ','
3522 From Platform SDK
3524 reserved = '\' | '/' | '|' | ':' | '"' | '<' | '>'
3528 // NOTE: If you change these strings, you must change the associated error message
3529 const string invalidFirstSubPathCharacters = @"\./";
3530 const string invalidLastSubPathCharacters = @"\./";
3531 const string invalidSubPathCharactersString = @"\?:*""<>|";
3532 static char[] s_invalidSubPathCharactersArray = invalidSubPathCharactersString.ToCharArray();
3534 // Return null if the subPath represents the current directory, for example:
3535 // path=""
3536 // path=" "
3537 // path="."
3538 // path="./"
3539 internal static string NormalizeLocationSubPath(string subPath, IConfigErrorInfo errorInfo) {
3540 // if subPath is null or empty, it is the current dir
3541 if (String.IsNullOrEmpty(subPath))
3542 return null;
3544 // if subPath=".", it is the current dir
3545 if (subPath == ".")
3546 return null;
3548 // do not allow whitespace in front of subPath, as the OS
3549 // handles beginning and trailing whitespace inconsistently
3550 string trimmedSubPath = subPath.TrimStart();
3551 if (trimmedSubPath.Length != subPath.Length) {
3552 throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_first_character), errorInfo);
3555 // do not allow problematic starting characters
3556 if (invalidFirstSubPathCharacters.IndexOf(subPath[0]) != -1) {
3557 throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_first_character), errorInfo);
3560 // do not allow whitespace at end of subPath, as the OS
3561 // handles beginning and trailing whitespace inconsistently
3562 trimmedSubPath = subPath.TrimEnd();
3563 if (trimmedSubPath.Length != subPath.Length) {
3564 throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_last_character), errorInfo);
3567 // the file system ignores trailing '.', '\', or '/', so do not allow it in a location subpath specification
3568 if (invalidLastSubPathCharacters.IndexOf(subPath[subPath.Length-1]) != -1) {
3569 throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_last_character), errorInfo);
3572 // combination of URI reserved characters and OS invalid filename characters, minus / (allowed reserved character)
3573 if (subPath.IndexOfAny(s_invalidSubPathCharactersArray) != -1) {
3574 throw new ConfigurationErrorsException(SR.GetString(SR.Config_location_path_invalid_character), errorInfo);
3577 return subPath;
3582 // Return the SectionRecord for a section.
3583 // If the record does not exist, return null.
3584 // Throw cached errors if the section is in error and permitErrors == false.
3586 protected SectionRecord GetSectionRecord(string configKey, bool permitErrors) {
3587 SectionRecord sectionRecord;
3589 if (_sectionRecords != null) {
3590 sectionRecord = (SectionRecord) _sectionRecords[configKey];
3592 else {
3593 sectionRecord = null;
3596 if (sectionRecord != null && !permitErrors) {
3597 sectionRecord.ThrowOnErrors();
3600 return sectionRecord;
3603 // Return an existing SectionRecord, or create one if one does not exist.
3604 // Propagate the Locked bit from parent
3605 protected SectionRecord EnsureSectionRecord(string configKey, bool permitErrors) {
3606 return EnsureSectionRecordImpl(configKey, permitErrors, true);
3609 // Return an existing SectionRecord, or create one if one does not exist.
3610 // Do not propagate the Locked bit from parent, because caller will check
3611 // himself later.
3612 protected SectionRecord EnsureSectionRecordUnsafe(string configKey, bool permitErrors) {
3613 return EnsureSectionRecordImpl(configKey, permitErrors, false);
3616 // Return an existing SectionRecord, or create one if one does not exist.
3617 // If desired, set the lock settings based on parent configs.
3618 private SectionRecord EnsureSectionRecordImpl(string configKey, bool permitErrors, bool setLockSettings) {
3619 SectionRecord sectionRecord = GetSectionRecord(configKey, permitErrors);
3620 if (sectionRecord == null) {
3621 lock (this) {
3622 if (_sectionRecords == null) {
3623 _sectionRecords = new Hashtable();
3625 else {
3626 sectionRecord = GetSectionRecord(configKey, permitErrors);
3629 if (sectionRecord == null) {
3631 sectionRecord = new SectionRecord(configKey);
3633 _sectionRecords.Add(configKey, sectionRecord);
3637 if (setLockSettings) {
3638 // Get the lock mode from parent configs
3639 OverrideMode parentMode = OverrideMode.Inherit;
3640 OverrideMode childLockMode = OverrideMode.Inherit;
3642 parentMode = ResolveOverrideModeFromParent(configKey, out childLockMode);
3644 sectionRecord.ChangeLockSettings(parentMode, childLockMode);
3648 return sectionRecord;
3651 private bool HasFactoryRecords {
3652 get {
3653 return _factoryRecords != null;
3657 internal FactoryRecord GetFactoryRecord(string configKey, bool permitErrors) {
3658 FactoryRecord factoryRecord;
3659 if (_factoryRecords == null) {
3660 return null;
3663 factoryRecord = (FactoryRecord) _factoryRecords[configKey];
3664 if (factoryRecord != null && !permitErrors) {
3665 factoryRecord.ThrowOnErrors();
3668 return factoryRecord;
3671 // Only create a _factories hashtable when necessary.
3672 // Most config records won't have factories, so we can save 120 bytes
3673 // per record by creating the table on demand.
3674 protected Hashtable EnsureFactories() {
3675 if (_factoryRecords == null) {
3676 _factoryRecords = new Hashtable();
3679 return _factoryRecords;
3682 private ArrayList EnsureLocationSections() {
3683 if (_locationSections == null) {
3684 _locationSections = new ArrayList();
3687 return _locationSections;
3690 // Return true if there is no unique configuration information in this record.
3691 internal bool IsEmpty {
3692 get {
3693 return
3694 _parent != null
3695 && !_initErrors.HasErrors(false)
3696 && (_sectionRecords == null || _sectionRecords.Count == 0)
3697 && (_factoryRecords == null || _factoryRecords.Count == 0)
3698 && (_locationSections == null || _locationSections.Count == 0);
3702 static internal string NormalizeConfigSource(string configSource, IConfigErrorInfo errorInfo) {
3703 if (String.IsNullOrEmpty(configSource)) {
3704 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
3707 string trimmedConfigSource = configSource.Trim();
3708 if (trimmedConfigSource.Length != configSource.Length) {
3709 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
3712 if (configSource.IndexOf('/') != -1) {
3713 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_chars), errorInfo);
3716 if (String.IsNullOrEmpty(configSource) || System.IO.Path.IsPathRooted(configSource)) {
3717 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_invalid_format), errorInfo);
3720 return configSource;
3723 protected object MonitorStream(string configKey, string configSource, string streamname) {
3724 lock (this) {
3725 if (_flags[Closed]) {
3726 return null;
3729 StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
3730 if (streamInfo != null) {
3731 if (streamInfo.SectionName != configKey) {
3732 throw new ConfigurationErrorsException(SR.GetString(SR.Config_source_cannot_be_shared, streamname));
3735 if (streamInfo.IsMonitored) {
3736 return streamInfo.Version;
3739 else {
3740 streamInfo = new StreamInfo(configKey, configSource, streamname);
3741 ConfigStreamInfo.StreamInfos.Add(streamname, streamInfo);
3746 // Call the host outside the lock to avoid deadlock.
3748 object version = Host.GetStreamVersion(streamname);
3750 StreamChangeCallback callbackDelegate = null;
3752 lock (this) {
3753 if (_flags[Closed]) {
3754 return null;
3757 StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
3758 if (streamInfo.IsMonitored) {
3759 return streamInfo.Version;
3762 streamInfo.IsMonitored = true;
3763 streamInfo.Version = version;
3765 if (_flags[SupportsChangeNotifications]) {
3766 if (ConfigStreamInfo.CallbackDelegate == null) {
3767 ConfigStreamInfo.CallbackDelegate = new StreamChangeCallback(this.OnStreamChanged);
3770 callbackDelegate = ConfigStreamInfo.CallbackDelegate;
3774 if (_flags[SupportsChangeNotifications]) {
3775 Host.StartMonitoringStreamForChanges(streamname, callbackDelegate);
3778 return version;
3781 private void OnStreamChanged(string streamname) {
3782 bool notifyChanged;
3783 StreamInfo streamInfo;
3784 string sectionName;
3786 lock (this) {
3787 if (_flags[Closed])
3788 return;
3790 streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[streamname];
3791 if (streamInfo == null || !streamInfo.IsMonitored)
3792 return;
3794 sectionName = streamInfo.SectionName;
3797 if (sectionName == null) {
3798 notifyChanged = true;
3800 else {
3801 FactoryRecord factoryRecord = FindFactoryRecord(sectionName, false);
3802 notifyChanged = factoryRecord.RestartOnExternalChanges;
3805 if (notifyChanged) {
3806 _configRoot.FireConfigChanged(_configPath);
3808 else {
3809 _configRoot.ClearResult(this, sectionName, false);
3813 // ValidateUniqueConfigSource
3815 // Validate that the configSource is unique for this particular
3816 // configKey. This looks up at the parents and makes sure it is
3817 // unique. It if is in a child, then it's check will find this
3818 // one. If it is in a peer, then we don't care as much, since it
3819 // will not affect Merge and UnMerge
3821 // See VSWhidbey 460219 for details.
3823 private void ValidateUniqueConfigSource(
3824 string configKey, string configSourceStreamName, string configSourceArg, IConfigErrorInfo errorInfo) {
3827 // Detect if another section in this file is using the same configSource
3828 // with has a different section name.
3830 lock (this) {
3831 if (ConfigStreamInfo.HasStreamInfos) {
3832 StreamInfo streamInfo = (StreamInfo) ConfigStreamInfo.StreamInfos[configSourceStreamName];
3833 if (streamInfo != null && streamInfo.SectionName != configKey) {
3834 throw new ConfigurationErrorsException(
3835 SR.GetString(SR.Config_source_cannot_be_shared, configSourceArg),
3836 errorInfo);
3841 ValidateUniqueChildConfigSource(configKey, configSourceStreamName, configSourceArg, errorInfo);
3844 protected void ValidateUniqueChildConfigSource(
3845 string configKey, string configSourceStreamName, string configSourceArg, IConfigErrorInfo errorInfo) {
3848 // Detect if a parent config file is using the same config source stream.
3850 BaseConfigurationRecord current;
3851 if (IsLocationConfig) {
3852 current = _parent._parent;
3854 else {
3855 current = _parent;
3858 while (!current.IsRootConfig) {
3859 lock (current) {
3860 if (current.ConfigStreamInfo.HasStreamInfos) {
3861 StreamInfo streamInfo = (StreamInfo) current.ConfigStreamInfo.StreamInfos[configSourceStreamName];
3862 if (streamInfo != null) {
3863 throw new ConfigurationErrorsException(
3864 SR.GetString(SR.Config_source_parent_conflict, configSourceArg),
3865 errorInfo);
3870 current = current.Parent;
3874 // Recursively clear the result.
3875 // If forceEvaluation == true, force a rescan of the config file to find
3876 // the section.
3877 // Requires the hierarchy lock to be acquired (hl)
3878 internal void hlClearResultRecursive(string configKey, bool forceEvaluatation) {
3879 SectionRecord sectionRecord;
3881 // Refresh it's factory Record
3882 RefreshFactoryRecord(configKey);
3884 // Clear any stored result in the section
3885 sectionRecord = GetSectionRecord(configKey, false);
3886 if (sectionRecord != null) {
3887 sectionRecord.ClearResult();
3889 // VSWhidbey 535724: Need to clear all RawXml so that when GetSectionXmlReader
3890 // is called later it will reload the file.
3891 sectionRecord.ClearRawXml();
3895 // If we need to reevaluate, add a dummy file input so
3896 // that we open the file on the next evaluation
3898 if (forceEvaluatation && !IsInitDelayed && !String.IsNullOrEmpty(ConfigStreamInfo.StreamName)) {
3899 if (_flags[SupportsPath]) {
3900 throw ExceptionUtil.UnexpectedError("BaseConfigurationRecord::hlClearResultRecursive");
3903 FactoryRecord factoryRecord = FindFactoryRecord(configKey, false);
3904 if (factoryRecord != null && !factoryRecord.IsGroup) {
3905 configKey = factoryRecord.ConfigKey;
3906 sectionRecord = EnsureSectionRecord(configKey, false);
3907 if (!sectionRecord.HasFileInput) {
3908 SectionXmlInfo sectionXmlInfo = new SectionXmlInfo(
3909 configKey, _configPath, _configPath, null,
3910 ConfigStreamInfo.StreamName, 0, null, null,
3911 null, null, null, null,
3912 null, OverrideModeSetting.LocationDefault, false);
3914 SectionInput fileInput = new SectionInput(sectionXmlInfo, null);
3915 sectionRecord.AddFileInput(fileInput);
3920 // Recurse
3921 if (_children != null) {
3922 IEnumerable children = _children.Values;
3923 foreach (BaseConfigurationRecord child in children) {
3924 child.hlClearResultRecursive(configKey, forceEvaluatation);
3929 // Returns a child record.
3930 // Requires the hierarchy lock to be acquired (hl)
3931 internal BaseConfigurationRecord hlGetChild(string configName) {
3932 if (_children == null)
3933 return null;
3935 return (BaseConfigurationRecord) _children[configName];
3938 // Adds a child record.
3939 // Requires the hierarchy lock to be acquired (hl)
3940 internal void hlAddChild(string configName, BaseConfigurationRecord child) {
3941 if (_children == null) {
3942 _children = new Hashtable(StringComparer.OrdinalIgnoreCase);
3945 _children.Add(configName, child);
3948 // Removes a child record.
3949 // Requires the hierarchy lock to be acquired (hl)
3950 internal void hlRemoveChild(string configName) {
3951 if (_children != null) {
3952 _children.Remove(configName);
3956 // Removes true if a child record is needed for a
3957 // child config path.
3958 // Requires the hierarchy lock to be acquired (hl)
3959 internal bool hlNeedsChildFor(string configName) {
3960 // Always return true for root config record
3961 if (IsRootConfig)
3962 return true;
3964 // Never create a child record when the parent has an exception.
3965 if (HasInitErrors) {
3966 return false;
3969 string childConfigPath = ConfigPathUtility.Combine(_configPath, configName);
3971 try {
3972 using (Impersonate()) {
3973 // check host if required
3974 if (Host.IsConfigRecordRequired(childConfigPath)) {
3975 return true;
3979 catch {
3980 // Don't allow frames up the stack to run exception filters while impersonated.
3981 throw;
3984 // see if there's a location
3985 if (_flags[SupportsLocation]) {
3986 BaseConfigurationRecord configRecord = this;
3988 while (!configRecord.IsRootConfig) {
3989 if (configRecord._locationSections != null) {
3990 configRecord.ResolveLocationSections();
3991 foreach (LocationSectionRecord locationSectionRecord in configRecord._locationSections) {
3992 if (UrlPath.IsEqualOrSubpath(childConfigPath, locationSectionRecord.SectionXmlInfo.TargetConfigPath)) {
3993 return true;
3998 configRecord = configRecord._parent;
4002 return false;
4005 // Close the record. An explicit close is needed
4006 // in order to stop monitoring streams used by
4007 // this record. Stream monitors cause this record
4008 // to be rooted in the GC heap.
4010 // Note that we purposely do not cleanup the child/parent
4011 // hierarchy. This is so that a config system which has
4012 // a pointer to this record can still call GetSection on
4013 // it while another thread closes it.
4014 internal void CloseRecursive() {
4015 if (!_flags[Closed]) {
4016 bool doClose = false;
4017 HybridDictionary streamInfos = null;
4018 StreamChangeCallback callbackDelegate = null;
4020 lock (this) {
4021 if (!_flags[Closed]) {
4022 _flags[Closed] = true;
4023 doClose = true;
4025 if (!IsLocationConfig && ConfigStreamInfo.HasStreamInfos) {
4026 callbackDelegate = ConfigStreamInfo.CallbackDelegate;
4027 streamInfos = ConfigStreamInfo.StreamInfos;
4029 ConfigStreamInfo.CallbackDelegate = null;
4030 ConfigStreamInfo.ClearStreamInfos();
4035 if (doClose) {
4036 // no hierarchy lock is needed to access _children here,
4037 // as it has already been detached from the hierarchy tree
4038 if (_children != null) {
4039 foreach (BaseConfigurationRecord child in _children.Values) {
4040 child.CloseRecursive();
4044 if (streamInfos != null) {
4045 foreach (StreamInfo streamInfo in streamInfos.Values) {
4046 if (streamInfo.IsMonitored) {
4047 Host.StopMonitoringStreamForChanges(streamInfo.StreamName, callbackDelegate);
4048 streamInfo.IsMonitored = false;
4056 internal string FindChangedConfigurationStream() {
4057 BaseConfigurationRecord configRecord = this;
4058 while (!configRecord.IsRootConfig) {
4059 lock (configRecord) {
4060 if (configRecord.ConfigStreamInfo.HasStreamInfos) {
4061 foreach (StreamInfo streamInfo in configRecord.ConfigStreamInfo.StreamInfos.Values) {
4062 if (streamInfo.IsMonitored && HasStreamChanged(streamInfo.StreamName, streamInfo.Version)) {
4063 return streamInfo.StreamName;
4069 configRecord = configRecord._parent;
4072 return null;
4075 private bool HasStreamChanged(string streamname, object lastVersion) {
4076 object currentVersion = Host.GetStreamVersion(streamname);
4078 if (lastVersion != null) {
4079 return (currentVersion == null || !lastVersion.Equals(currentVersion));
4081 else {
4082 return currentVersion != null;
4086 // RuntimeConfigurationRecord will override it in order to Assert Fulltrust before calling the provider.
4087 // See VSWhidbey 429996.
4088 protected virtual string CallHostDecryptSection(string encryptedXml, ProtectedConfigurationProvider protectionProvider, ProtectedConfigurationSection protectedConfig) {
4089 return Host.DecryptSection(encryptedXml, protectionProvider, protectedConfig);
4092 protected virtual XmlNode CallHostProcessRawXml(XmlNode rawXml, ConfigurationBuilder configBuilder) {
4093 if (ConfigBuilderHost != null) {
4094 return ConfigBuilderHost.ProcessRawXml(rawXml, configBuilder);
4097 return rawXml;
4100 protected virtual ConfigurationSection CallHostProcessConfigurationSection(ConfigurationSection configSection, ConfigurationBuilder configBuilder) {
4101 if (ConfigBuilderHost != null) {
4102 return ConfigBuilderHost.ProcessConfigurationSection(configSection, configBuilder);
4105 return configSection;
4108 static internal string ValidateConfigBuilderAttribute(string configBuilder, IConfigErrorInfo errorInfo) {
4109 if (String.IsNullOrEmpty(configBuilder)) {
4110 throw new ConfigurationErrorsException(SR.GetString(SR.Config_builder_invalid_format), errorInfo);
4113 return configBuilder;
4116 static internal string ValidateProtectionProviderAttribute(string protectionProvider, IConfigErrorInfo errorInfo) {
4117 if (String.IsNullOrEmpty(protectionProvider)) {
4118 throw new ConfigurationErrorsException(SR.GetString(SR.Protection_provider_invalid_format), errorInfo);
4121 return protectionProvider;
4124 private ConfigXmlReader DecryptConfigSection(ConfigXmlReader reader, ProtectedConfigurationProvider protectionProvider) {
4125 ConfigXmlReader clone = reader.Clone();
4126 IConfigErrorInfo err = (IConfigErrorInfo)clone;
4127 string encryptedXml = null;
4128 string clearTextXml = null;
4129 XmlNodeType nodeType;
4131 clone.Read();
4133 // Save the file and line at the top of the section
4135 string filename = err.Filename;
4136 int lineNumber = err.LineNumber;
4137 int sectionLineNumber = lineNumber;
4139 if (clone.IsEmptyElement) {
4140 throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_not_found), filename, lineNumber);
4143 //////////////////////////////////////////////////////////
4144 // Find the <EncryptedData> node
4145 for (;;) {
4146 clone.Read(); // Keep reading till we find a relavant node
4148 nodeType = clone.NodeType;
4150 if (nodeType == XmlNodeType.Element && clone.Name == "EncryptedData") { // Found it!
4151 break;
4154 if (nodeType == XmlNodeType.EndElement) {
4155 throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_not_found), filename, lineNumber);
4157 else if (nodeType != XmlNodeType.Comment && nodeType != XmlNodeType.Whitespace) {
4158 // some other unexpected content
4159 throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_is_in_invalid_format), filename, lineNumber);
4163 //////////////////////////////////////////////////////////
4164 // Do the decryption
4166 // Save the line at the top of the <EncryptedData> node
4167 lineNumber = err.LineNumber;
4169 encryptedXml = clone.ReadOuterXml();
4170 try {
4171 clearTextXml = CallHostDecryptSection(encryptedXml, protectionProvider, ProtectedConfig);
4172 } catch (Exception e) {
4173 throw new ConfigurationErrorsException(SR.GetString(SR.Decryption_failed, protectionProvider.Name, e.Message), e, filename, lineNumber);
4176 // Detect if there is any XML left over after <EncryptedData>
4177 do {
4178 nodeType = clone.NodeType;
4180 if (nodeType == XmlNodeType.EndElement) {
4181 break;
4183 else if (nodeType != XmlNodeType.Comment && nodeType != XmlNodeType.Whitespace) {
4184 // Got other unexpected content
4185 throw new ConfigurationErrorsException(SR.GetString(SR.EncryptedNode_is_in_invalid_format), filename, lineNumber);
4187 } while (clone.Read());
4189 // Create a new reader, using the position of the original reader
4190 return new ConfigXmlReader(clearTextXml, filename, sectionLineNumber, true);
4193 private ConfigXmlReader ProcessRawXml(ConfigXmlReader reader, ConfigurationBuilder configBuilder) {
4194 IConfigErrorInfo err = (IConfigErrorInfo)reader;
4195 XmlNode processedXml = null;
4197 string filename = err.Filename;
4198 int lineNumber = err.LineNumber;
4200 try {
4201 XmlDocument doc = new XmlDocument();
4202 doc.PreserveWhitespace = true;
4203 doc.LoadXml(reader.RawXml);
4204 processedXml = CallHostProcessRawXml(doc.DocumentElement, configBuilder);
4206 catch (Exception e) {
4207 throw new ConfigurationErrorsException(SR.GetString(SR.ConfigBuilder_processXml_error, configBuilder.Name, e.Message), e, filename, lineNumber);
4210 return new ConfigXmlReader(processedXml.OuterXml, filename, lineNumber, true);
4213 // ConfigContext
4215 // Retrieve the context for the config
4217 internal object ConfigContext
4221 if (!_flags[ContextEvaluated]) {
4222 // Retrieve context for Path
4223 _configContext = Host.CreateConfigurationContext(ConfigPath, LocationSubPath);
4224 _flags[ContextEvaluated] = true;
4227 return _configContext;
4231 // ThrowIfParseErrors
4233 // Throw if there were parse errors detected
4235 private void ThrowIfParseErrors(ConfigurationSchemaErrors schemaErrors) {
4236 schemaErrors.ThrowIfErrors(ClassFlags[ClassIgnoreLocalErrors]);
4240 // RecordSupportsLocation
4242 // Does it make sense to put use location tags in this file?
4243 // In the web case this is true at any level. In the exe case
4244 // this is only true for machine.config (since machine.config
4245 // can really be used for any scenario)
4247 internal bool RecordSupportsLocation {
4248 get {
4249 return (_flags[SupportsLocation] || IsMachineConfig);
4254 // Note: Some of the per-attribute encryption stuff is moved to the end of the file to minimize
4255 // FI merging conflicts
4257 const string ConfigurationBuildersSectionTypeName = "System.Configuration.ConfigurationBuildersSection, " + AssemblyRef.SystemConfiguration;
4258 internal const string RESERVED_SECTION_CONFIGURATION_BUILDERS = "configBuilders";
4259 Type ConfigurationBuildersSectionType = Type.GetType(ConfigurationBuildersSectionTypeName);
4260 const string ProtectedConfigurationSectionTypeName = "System.Configuration.ProtectedConfigurationSection, " + AssemblyRef.SystemConfiguration;
4261 internal const string RESERVED_SECTION_PROTECTED_CONFIGURATION = "configProtectedData";
4262 internal const string Microsoft_CONFIGURATION_SECTION = ConfigurationStringConstants.WinformsApplicationConfigurationSectionName;
4263 const string SystemConfigurationSectionTypeName = "System.Configuration.AppSettingsSection, " + AssemblyRef.SystemConfiguration;
4265 internal static bool IsImplicitSection(string configKey) {
4266 if (string.Equals(configKey, RESERVED_SECTION_PROTECTED_CONFIGURATION, StringComparison.Ordinal) ||
4267 //string.Equals(configKey, RESERVED_SECTION_CONFIGURATION_BUILDERS, StringComparison.Ordinal) ||
4268 string.Equals(configKey, Microsoft_CONFIGURATION_SECTION, StringComparison.Ordinal)) {
4269 return true;
4271 else {
4272 return false;
4277 // Add implicit sections to the factory list.
4278 // If factoryList == null, then add to the config record's factory list.
4280 private void AddImplicitSections(Hashtable factoryList) {
4281 // Add implicit sections to the factoryList if we're under the root
4282 // (e.g. if we're in machine.config)
4283 if (_parent.IsRootConfig) {
4285 if (factoryList == null) {
4286 factoryList = EnsureFactories();
4289 FactoryRecord factoryRecord = (FactoryRecord)factoryList[RESERVED_SECTION_PROTECTED_CONFIGURATION];
4291 // If the user has mistakenly declared an implicit section, we should leave the factoryRecord
4292 // alone because it contains the error and the error will be thrown later.
4293 if (factoryRecord != null) {
4294 Debug.Assert(factoryRecord.HasErrors, "If the user has mistakenly declared an implicit section, we should have recorded an error.");
4296 else {
4297 factoryList[RESERVED_SECTION_PROTECTED_CONFIGURATION] =
4298 new FactoryRecord(
4299 RESERVED_SECTION_PROTECTED_CONFIGURATION, // configKey
4300 string.Empty, // group
4301 RESERVED_SECTION_PROTECTED_CONFIGURATION, // name
4302 ProtectedConfigurationSectionTypeName, // factoryTypeName
4303 true, // allowLocation
4304 ConfigurationAllowDefinition.Everywhere, // allowDefinition
4305 ConfigurationAllowExeDefinition.MachineToApplication, // allowExeDefinition
4306 OverrideModeSetting.SectionDefault, // overrideModeDefault
4307 true, // restartOnExternalChanges
4308 true, // requirePermission
4309 true, // isFromTrustedConfig
4310 true, // isUndeclared
4311 null, // filename
4312 -1); // lineNumber
4315 factoryRecord = (FactoryRecord)factoryList[Microsoft_CONFIGURATION_SECTION];
4317 // If the user has mistakenly declared an implicit section, we should leave the factoryRecord
4318 // alone because it contains the error and the error will be thrown later.
4319 if (factoryRecord != null)
4321 Debug.Assert(factoryRecord.HasErrors, "If the user has mistakenly declared an implicit section, we should have recorded an error.");
4323 else
4325 factoryList[Microsoft_CONFIGURATION_SECTION] =
4326 new FactoryRecord(
4327 Microsoft_CONFIGURATION_SECTION, // configKey
4328 string.Empty, // group
4329 Microsoft_CONFIGURATION_SECTION, // name
4330 SystemConfigurationSectionTypeName, // factoryTypeName
4331 true, // allowLocation
4332 ConfigurationAllowDefinition.Everywhere, // allowDefinition
4333 ConfigurationAllowExeDefinition.MachineToApplication, // allowExeDefinition
4334 OverrideModeSetting.SectionDefault, // overrideModeDefault
4335 true, // restartOnExternalChanges
4336 true, // requirePermission
4337 true, // isFromTrustedConfig
4338 true, // isUndeclared
4339 null, // filename
4340 -1); // lineNumber
4345 // We reserve all attribute names starting with config or lock
4346 internal static bool IsReservedAttributeName(string name) {
4347 if (StringUtil.StartsWith(name, "config") ||
4348 StringUtil.StartsWith(name, "lock")) {
4349 return true;
4351 else {
4352 return false;
4356 protected class ConfigRecordStreamInfo {
4357 private bool _hasStream; // does the stream exist?
4358 private string _streamname; // name of the stream of this record
4359 private object _streamVersion; // version of the stream
4360 private Encoding _encoding; // encoding of the stream
4361 private StreamChangeCallback _callbackDelegate; // host delegate to callback to when stream has changed
4362 private HybridDictionary _streamInfos; // streamname -> StreamInfo. It'll also contain the main stream pointed to by _streamname
4364 internal ConfigRecordStreamInfo() {
4365 // default encoding
4366 _encoding = Encoding.UTF8;
4369 internal bool HasStream {
4370 get { return _hasStream; }
4371 set { _hasStream = value; }
4374 internal string StreamName {
4375 get { return _streamname; }
4376 set { _streamname = value; }
4379 internal object StreamVersion {
4380 get { return _streamVersion; }
4381 set { _streamVersion = value; }
4384 internal Encoding StreamEncoding {
4385 get { return _encoding; }
4386 set { _encoding = value; }
4389 internal StreamChangeCallback CallbackDelegate {
4390 get { return _callbackDelegate; }
4391 set { _callbackDelegate = value; }
4394 internal HybridDictionary StreamInfos {
4395 get {
4396 if (_streamInfos == null) {
4397 _streamInfos = new HybridDictionary(true);
4400 return _streamInfos;
4404 internal bool HasStreamInfos {
4405 get { return _streamInfos != null; }
4408 internal void ClearStreamInfos() {
4409 _streamInfos = null;
4413 #if DBG
4414 // For Debugging only
4415 internal string[] Keys {
4416 get {
4417 string[] keys = new string[StreamInfos.Count];
4418 StreamInfos.Keys.CopyTo(keys, 0);
4419 return keys;
4422 #endif
4425 private class IndirectLocationInputComparer : IComparer<SectionInput> {
4426 public int Compare(SectionInput x, SectionInput y) {
4427 // We have to sort the indirect inputs
4428 // 1. First by the location tag's target config path, and if they're the same,
4429 // 2. Then by the location tag's definition config path.
4431 // In the final sorted list, a child will be smaller than a parent.
4433 Debug.Assert(x.SectionXmlInfo.ConfigKey == y.SectionXmlInfo.ConfigKey);
4435 if (Object.ReferenceEquals(x, y)) {
4436 // Check if they're the same object.
4437 return 0;
4440 string xTargetConfigPath = x.SectionXmlInfo.TargetConfigPath;
4441 string yTargetConfigPath = y.SectionXmlInfo.TargetConfigPath;
4443 // First compare using location tag's target config path:
4444 if (UrlPath.IsSubpath(xTargetConfigPath, yTargetConfigPath)) {
4445 // yTargetConfigPath is a child path of xTargetConfigPath, so y < x
4446 return 1;
4448 else if (UrlPath.IsSubpath(yTargetConfigPath, xTargetConfigPath)) {
4449 // xTargetConfigPath is a child path of yTargetConfigPath, so x < y
4450 return -1;
4452 else {
4453 // Because all indirect inputs must be pointing to nodes along a
4454 // single branch of config hierarchy, so if the above two cases
4455 // aren't true, then the two target config path must be equal;
4456 // in another word, they should not be siblings.
4457 Debug.Assert(StringUtil.EqualsIgnoreCase(yTargetConfigPath, xTargetConfigPath));
4459 string xDefinitionConfigPath = x.SectionXmlInfo.DefinitionConfigPath;
4460 string yDefinitionConfigPath = y.SectionXmlInfo.DefinitionConfigPath;
4462 // Then compare using where the location tag is defined.
4463 if (UrlPath.IsSubpath(xDefinitionConfigPath, yDefinitionConfigPath)) {
4464 // yDefinitionConfigPath is a child path of xDefinitionConfigPath, so y < x
4465 return 1;
4467 else if (UrlPath.IsSubpath(yDefinitionConfigPath, xDefinitionConfigPath)) {
4468 // xDefinitionConfigPath is a child path of yDefinitionConfigPath, so x < y
4469 return -1;
4471 else {
4472 Debug.Assert(false,
4473 "It's not possible for two location input to come from the same config file and point to the same target");
4474 return 0;
4480 internal Configuration CurrentConfiguration {
4481 get {
4482 return _configRoot.CurrentConfiguration;
4486 internal bool TypeStringTransformerIsSet {
4487 get {
4488 return (CurrentConfiguration == null) ? false : CurrentConfiguration.TypeStringTransformerIsSet;
4492 internal bool AssemblyStringTransformerIsSet {
4493 get {
4494 return (CurrentConfiguration == null) ? false : CurrentConfiguration.AssemblyStringTransformerIsSet;
4498 internal System.Func<string, string> TypeStringTransformer {
4499 get {
4500 return (CurrentConfiguration == null) ? null : CurrentConfiguration.TypeStringTransformer;
4504 internal System.Func<string, string> AssemblyStringTransformer {
4505 get {
4506 return (CurrentConfiguration == null) ? null : CurrentConfiguration.AssemblyStringTransformer;
4510 internal FrameworkName TargetFramework {
4511 get {
4512 return (CurrentConfiguration == null) ? null : CurrentConfiguration.TargetFramework;
4516 internal Stack SectionsStack {
4517 get {
4518 return (CurrentConfiguration == null) ? (new Stack()) : CurrentConfiguration.SectionsStack;